智汇 IT
城启新篇

数据库索引藏在查询路径里的隐形成本

上周跟一个做电商运维的朋友聊天,他说大促前压测,给全表的索引删了重加,以为能清掉碎片,结果上线当天直接把订单查询打崩了。

这事没那么玄。

很多人对数据库索引的认知还停留在“加了查询就快”的层面,真出问题的时候根本摸不着北。

你以为的快,其实是提前交的过路费

说白了,数据库索引根本不是什么免费的优化工具。你每给一个字段加索引,写操作的时候就要多维护一份排序好的数据结构。每次插新用户、改手机号,得往主表写数据,还得往索引对应的B树节点里插新的键值,调整树的平衡。这部分开销,很多人算查询收益的时候自动就忽略了。

数据库B树索引结构节点拆分示意图
数据库B树索引结构节点拆分示意图

之前见过有个团队,为了让列表页的筛选快一点,给单张200万行的业务表加了7个单值索引,结果每天的批量用户数据同步任务,跑的时间从40分钟涨到3个半小时。为啥?因为每更新一行,就要同步改7个索引的节点。写操作的RT直接翻了快5倍。他们一开始还以为是服务器CPU不够,换了更高配的机器,没用。后来拆了3个没人用的索引,同步时间直接掉回了正常水平。

说真的,我见过太多团队把索引当万能药,查询慢了就加索引,从来不算算背后的写操作代价。磁盘空间占了多少,写延迟涨了多少,从来没人列个表算清楚。

选错索引,比没加还坑

麻烦在于,数据库的优化器不是你肚子里的蛔虫。你明明建了三个索引,它偏偏选了那个区分度最差的。比如你给订单表的状态字段(总共就待支付、已支付、已取消三个值)加了索引,又给用户ID加了索引,当你查某个用户的已支付订单的时候,优化器要是抽风选了状态字段的索引,那它扫出来的可能是全表30%的行,再回表匹配用户ID,速度比直接全表扫还慢。

回表的成本,是很多人建索引时根本没算过的账。啥叫回表?就是你查的字段不在索引的覆盖范围里,拿到索引对应的主键值之后,还要再去主表把整行数据捞出来。如果索引的区分度够高,扫的行数少,回表的成本还能接受。要是扫的行数多,那来回跳着读主表的开销,比顺序扫全表还大。

数据库索引回表查询流程示意图
数据库索引回表查询流程示意图

之前有次排查慢查询,看到SQL里查用户的注册时间、消费金额,建的索引是用户ID+注册时间,结果每次查的时候,还要回表捞消费金额的字段。后来把消费金额加到索引的末尾,变成联合索引的一部分,同样的查询,耗时直接降了90%。根本不需要改SQL,只是调整了索引的字段顺序。

你说气人不气人?之前团队为了这个慢查询,专门给数据库升了8核的配置,钱花了,问题半毛钱没解决,就因为索引少带了一个字段。

那些没人说的索引取舍

首先,不是所有字段都值得加索引。比如表本身就只有几百行数据,你加十个索引,优化器大概率根本不会用。因为扫全表的成本比走索引还低,对吧?这时候加索引纯纯是给写操作添乱。

别死记硬背联合索引的最左匹配原则。很多人背了好几年这个规则,真到写SQL的时候,偏偏跳过最左的字段查后面的,然后过来问为啥索引没生效。说白了,索引是按字段顺序排好序的,你跳过最前面的排序键,后面的键值根本没有顺序可言,自然没法快速定位。

还有,索引不是建完就一劳永逸的。表的数据量涨了,索引的碎片多了,统计信息不准了,优化器选的索引可能就不对了。之前见过有张表,数据从10万涨到1000万,之前建的索引慢慢就失效了,因为统计信息里的区分度算错了,优化器以为走索引的成本更高,直接选了全表扫。这时候你就算手动强制走索引,说不定还会更慢。

不过话说回来,很多人总想着找一个万能的索引配置,能适配所有的查询场景。哪有那种好事?索引本质上是你针对自己的业务查询模式做的定制优化,你的查询逻辑变了,索引就得跟着调。没有放之四海而皆准的最佳实践。

上次跟那个大促出问题的朋友聊,他后来把所有业务表的索引列了个清单,每个索引都标清楚是对应哪条查询、哪个页面在用,三个月里删了快40%的无用索引,写操作的性能涨了一倍,查询速度还没掉。

免责声明:市场有风险,选择需谨慎!此文仅供参考,不作买卖依据。如有侵权请联系删除。
文章名称:数据库索引藏在查询路径里的隐形成本
文章链接:https://m.itcdj.com/it/326.html