数据库索引不是免费的性能加速器

上周和个做电商的后端工程师吃饭,他说最近大促前压测,接口延迟从200ms飙到2s,查了半天发现是运营上周加了三个筛选维度,开发图快,直接给每个维度的字段都加了普通索引。

你以为的索引是查得快的捷径

很多人对数据库索引的认知停留在课本里的“书的目录”比喻,觉得只要给常用查询的字段加个索引,就能从全表扫描的几万行数据里直接定位到需要的那几行,完全不用考虑背后的代价。说白了,现在主流数据库用的都是B+树结构的索引,你可以把它想象成一棵分层的多叉树,所有数据都存在叶子节点,非叶子节点只存路由信息,顺着节点找几次就能定位到目标数据的位置。
数据库B树索引节点拆分示意图
数据库B树索引节点拆分示意图
课本里从来不会告诉你,这棵树不是凭空长出来的。你每写一行数据,不光要往表里存原始数据,还要往所有关联的索引树里插对应的节点。插错位置就拆分。一拆分,页就分裂。IO量直接翻几倍。你以为的“加个索引就快”,本质是用写入时的额外开销,换查询时的时间节省,从来没有什么免费的午餐。

每加一个索引都在给写操作收税

刚才那个电商的案例,他那张订单表原来只有主键索引和用户ID索引,一共两个。加了三个筛选字段的索引之后,变成五个索引。每次插入一条订单,要同时维护五棵树的结构,保证每棵树的排序和路由都是正确的。压测的时候写入QPS直接掉了40%,查询倒是快了点,但写入扛不住大促的峰值,反而成了系统的瓶颈。 很多人只算查询的收益,不算写入的成本。你给一个有1000万行的表加索引,建索引的过程要扫全表,还要占临时存储,高峰期建索引甚至会把整个库的资源占满,拖垮所有线上业务。更别说索引本身的存储开销,同样的磁盘空间,装索引多了,能存的业务数据就少了,热数据的缓存命中率也会跟着下降。
数据库索引写操作开销对比图
数据库索引写操作开销对比图
之前见过个更夸张的,一张用户行为表加了12个索引,写入的时候要维护12棵树,写延迟比没加索引的时候还高两倍,后来删到只剩3个核心索引,整体性能反而上去了。你加的每一个索引,都是在给每次INSERT、UPDATE、DELETE加额外的工作量,没有例外。

别迷信联合索引的最左匹配

很多人背最左匹配背得滚瓜烂熟,上来就建联合索引,比如(a,b,c),觉得能覆盖各种组合查询。但很少有人想,这个索引的字段顺序到底是按什么排的?如果把低基数的字段放在最左,比如性别、是否删除这种只有两三个值的字段,那这个索引的区分度极低,查出来还是几万行数据,跟全表扫描差不了多少,完全白建。 还有人不知道最左匹配的边界条件,如果查询里在中间的字段用了范围查询,后面的字段就用不上索引了。比如where a=1 and b>10 and c=2,那c这个字段的索引就直接失效了,等于你白加了c的字段在联合索引里,平白占了存储,还增加了写入的维护成本。 还有人迷信覆盖索引,觉得把所有查询要的字段都塞到索引里,就能不用回表,速度最快。但很少有人算过,如果索引塞的字段太多,索引树的节点会变得特别宽,同样的内存页能存的节点数就变少,树的层级可能从3层涨到4层,查询一次要多一次IO,反而更慢。 后来那个做电商的工程师把多余的三个普通索引删了,换成了一个刚好覆盖运营筛选需求的联合索引,压测的时候写入QPS回到了原来的水平,查询延迟也没涨多少。 数据库索引从来不是越多越好。也不是越复杂越好。每次加索引之前,先算清楚账。你要的是整个系统的整体性能最优,不是某一个查询的速度变快。
免责声明:市场有风险,选择需谨慎!此文仅供参考,不作买卖依据。如有侵权请联系删除。
文章名称:数据库索引不是免费的性能加速器
文章链接:https://www.itcdj.com/it/164.html