凡是有人问数据库索引加速还是拖慢写入,我基本不会先甩答案,而是反问一句:你那张表是读得多还是写得多。索引这东西天生偏心,它拿写入的代价去换查询的速度。多数人只盯着 SELECT 从转圈变成眨眼,却没留意后台每插一行,都要顺手去好几个地方改账本。

数据库索引加速还是拖慢写入,答案藏在读写比里
读多写少的表,比如文章表、商品表、分类字典,加索引基本是纯赚。一天改不了几回,查询却成百上千次,哪怕多占一点磁盘也认了。反过来,日志表、采集表、订单流水这种一直在往里灌数据的,索引一多,写进去的每一行都要多干几份活,导入速度肉眼可见往下掉。所以别急着问索引好不好,先问这张表的读写比例大概是多少,心里有个数再动手。
它本质就是一本排好序的目录
翻过纸质词典的人都懂,找词快是因为它按拼音或者部首排好了。索引干的是同一件事,主流引擎用的是 B+树,叶子节点按顺序串起来,找一行不用从头翻到尾,顺着层级往下走几页就到。InnoDB 里主键索引的叶子节点直接挂着整行数据,二级索引只存索引列加主键值,所以走二级索引查别的字段,还得回表再捞一次。这个回表动作,就是很多人抱怨「明明建了索引还是慢」的根源之一。
写一笔,背后要记好几本账
插入一行看着轻巧,实际上每个索引都要跟着调整位置,页面满了还得分裂,分裂就意味着更多的随机写。更新更麻烦,改了索引列,等于删一条再插一条。唯一索引还要额外判重。这些开销平时被缓冲池挡着看不出来,一到大批量导入全暴露。做数据迁移的老手都干过一件事:导入前先把非必要的索引删掉,灌完再统一建回来,比边灌边维护快得多。
低选择性的列,建了等于白建
性别、是否删除、订单状态这种只有几个取值的列,单独建索引基本没用。优化器一看,走索引要扫掉半张表再回表,还不如直接顺序扫一遍,于是干脆不用你这个索引。索引白占空间,还白拖写入,典型的赔本买卖。唯一例外是覆盖索引:如果你的查询只取这几列,走二级索引反而比全表扫省,因为索引本身比整行窄。这种就得具体看语句,不能一概而论。
联合索引的顺序,别按感觉排
多列索引有最左前缀的规矩,第一个字段没出现在条件里,后面那些基本用不上。所以顺序怎么排是有讲究的:等值条件放前面,区分度高的放前面,范围条件(大于、小于、between)尽量往后放,因为它后面的列很难再吃到索引的便利。见过不少人一个查询建一个索引,五六个查询建五六个索引,表没多大,索引先堆成山,维护的时候自己都记不清哪个是哪个。
见过最离谱的一种加法
有一回帮人看一个采集站,表里十几个字段,几乎每一列都挂了索引,理由是「怕查得慢」。结果后台采集一批数据要跑很久,前台更新一条也卡,磁盘占用还一直往上走。后来把从没进过 WHERE 的、重复的、只在前缀里出现过的通通删掉,只留两三个真正用得上的组合,读写都顺了。这事说明加索引不难,难的是敢删。
怎么验证,别拍脑袋
改之前先看 EXPLAIN,重点盯 type、key、rows 这几列,type 是 ALL 就说明在全表扫;再看慢查询日志里到底是哪几条语句在拖后腿;show index 里的 cardinality 能大致看出这列的区分度,数值越接近行数说明越分散。手上有真实数据的话,在测试库上照着生产的量压一遍,比任何人的经验都靠得住。生产库上直接动手改,尤其挑业务高峰,属于给自己找事。
顺手清理,比不停新增更值钱
索引这东西是会过期的。功能改版之后,原来那条查询没人用了,索引还留着占位置;两个索引 (a) 和 (a,b),前者完全被后者覆盖,也是白留。隔段时间翻一翻,配合 sys schema 或者引擎自带的使用统计,把零调用的挑出来评估。删之前先在测试环境跑一轮,别一刀切。
绕回开头那个问题:索引让查询快,是因为它提前把顺序整理好了;它拖慢写入,也是因为每次写都要维护这份顺序,两边说的其实是同一件事。真要排个动手顺序,我会这么走:先摸清这张表的读写比,再挑出真正出现在查询条件里、区分度够的列,联合索引按等值和范围排好先后,上线前拿真实数据量验一遍,过半年回头清一次。至于「给每一列都加索引更保险」这种说法,听过就算了。
【版权声明】:本站内容若无意侵犯到您的权利,请及时与我们联系将尽快删除相关内容!