智汇 IT
城启新篇

数据库索引是需要算账的技术选择

上周在机房蹲点碰到个后端小哥,盯着慢查询日志骂了十分钟。

他那台数据库跑了快三年,存了小两千万的用户行为数据,本来查某个用户的历史操作只需要几十毫秒,最近加了个新的统计需求,要按行为类型和时间范围筛数据,他随手给两个字段各加了个索引,结果导入测试数据的时候卡得要死,查询也没快多少。

这事没那么玄,本质就是大家对数据库索引的认知,要么太神化,要么太随意。

索引到底省了什么时间

很多人对索引的认知停留在“加了就快”,但没人细想快在哪。

全表扫描是什么概念?就是数据库从表的第一行开始,一行一行翻,直到把符合条件的行找出来,数据量小的时候感觉不到,几百万行的表,扫一次可能要好几秒。

数据库索引的核心逻辑,是给要查的字段建了一套有序的快速查找结构,最常用的就是B+树。你要找某个值,不用翻遍所有行,顺着树的层级找几次就能定位到数据的位置,时间复杂度直接从O(n)降到了O(logn)。

举个最直观的例子,两千万行的表,log2(n)大概是24,也就是说最多找24次就能定位到目标数据,和扫两千万行的效率差了好几个数量级。

数据库B树索引查找路径示意图
数据库B树索引查找路径示意图

当然,这是理想状态下的效率。要是你查的条件匹配了表里超过20%的行,数据库优化器会直接放弃索引,转头去做全表扫描——毕竟顺着索引找完所有匹配行,还要回表查数据,反而比直接扫全表更慢。

加索引从来不是无本买卖

这是最多人踩的坑。

很多刚入行的开发者有个误区,觉得索引加得越多越好,写SQL的时候where后面跟什么字段就加什么索引,完全不管后面的代价。

麻烦在于,索引不是凭空存在的。你每次往表里插一行数据,得先把数据写到数据页里,再把这条记录的对应字段值,插到所有关联的索引结构里。

每多一个索引,写操作的额外开销就会叠加上去。

之前碰到过一个团队,给一张核心订单表加了7个索引,本来是想让各种查询场景都能跑快,结果月底批量导入历史订单的时候,导入速度从原来的10分钟变成了两个半小时,差点耽误了财务对账。

还有个容易被忽略的成本是存储空间。一张存了3T数据的业务表,上面建的索引可能就占了1T的空间,这些存储都是真金白银的成本,不是大风刮来的。

数据库索引写操作流程对比图
数据库索引写操作流程对比图

我见过最夸张的一张表,光索引就占了总存储的40%,写的时候要维护四套索引结构,最后把没用的索引删掉之后,写吞吐量直接涨了两倍,存储空间还省了好几百G。

很多人从一开始就用错了索引

就算你知道索引有代价,建的时候没多想,还是会踩一堆坑。

最常见的就是最左前缀的问题。比如你建了个联合索引(user_id, create_time, action_type),这个索引的排序是先按user_id排,user_id相同的情况下再按create_time排,以此类推。要是你写查询的时候只查action_type,或者where条件里没有user_id,这个索引根本用不上。

还有个坑是在索引字段上套函数。比如你想查2024年1月1号的所有数据,写了where date(create_time) = ‘2024-01-01’,这时候索引就失效了。说白了就是索引存的是create_time的原始时间值,你把字段值转成了日期,数据库没法顺着树直接找,只能乖乖全表扫。

更冤的是隐式类型转换。比如你索引字段是varchar类型的user_id,写查询的时候随手写了where user_id = 123,数据库会自动把user_id转成数字再比较,这时候索引直接就废了,很多人排查半天都找不到原因。

还有个反常识的点,索引不是什么字段都能加。如果一个字段的取值范围特别小,比如性别字段,只有男、女、未知三个值,就算你加了索引,查询的时候匹配的行可能占了总数据的三分之一,这时候数据库优化器根本不会选这个索引,加了纯纯是浪费写性能和存储空间。

不过话说回来,现在很多分布式数据库的索引逻辑已经和传统的关系型数据库不太一样了。比如有些用了LSM树结构的存储引擎,写的时候先把数据写到内存里的有序表,再批量刷到磁盘上,索引的写开销比传统的B+树低很多,但也不是没有代价——读的时候可能要查多层结构,偶尔会碰到读放大的问题,极端情况下还要做合并清理。

上周那个骂慢查询的小哥,最后把那表上没用的三个索引删了,针对统计需求建了个(action_type, create_time)的联合索引,慢查询直接从12秒降到了30毫秒,导入测试数据的速度也回到了正常水平。

他说之前总觉得索引是能解决所有查询慢的银弹,现在才明白,这东西从来不是免费的。

你选了什么,就要承担对应的代价,对吧?

免责声明:市场有风险,选择需谨慎!此文仅供参考,不作买卖依据。如有侵权请联系删除。
文章名称:数据库索引是需要算账的技术选择
文章链接:https://www.itcdj.com/news/703.html