加入收藏 | 设为首页 | 会员中心 | 我要投稿 站长网 (https://www.ijishu.cn/)- CDN、边缘计算、物联网、云计算、开发!
当前位置: 首页 > 运营中心 > 搜索优化 > 正文

13年模块开发血泪:B+树索引竟成搜索毒瘤

发布时间:2026-10-09 03:53:01 所属栏目:搜索优化 来源:DaWei
导读:凌晨三点十四分,报警群连炸了八条消息,订单查询平均响应飙到四千三百毫秒,而我面前那杯美式已经凉透。我打开 Grafana 面板,QPS 只有两百一十左右,慢查询日志里躺着一条 `SELECT FROM ord_mast WHERE stat = 'INIT' AND c
凌晨三点十四分,报警群连炸了八条消息,订单查询平均响应飙到四千三百毫秒,而我面前那杯美式已经凉透。
我打开 Grafana 面板,QPS 只有两百一十左右,慢查询日志里躺着一条 `SELECT FROM ord_mast WHERE stat = 'INIT' AND ctime BETWEEN ? AND ? LIMIT 5000;`,执行计划 text 显示 key 是 idx_stat_ctime,rows 预估为八十九万,Extra 里赫然写着 Using index condition; Using where。
这破索引根本就是披着羊皮的全表扫描。
运维老李昨天下午五点半拍着桌子冲我喊,"你们模块把 B+树当万金油,越用越毒"。他手里还攥着半张打印出来的 iostat,说 sdb 的 util 已经干到 98% w_await 两百三十毫秒。
我上个月十五号用 pt-query-digest 跑了一遍昨天全量日志,那条查初始状态订单的语句占总慢查的六成七,扫描行数三百七十二万,返回不到四千行。磁盘吞叶节点一层层翻,二级索引回表像拿勺子舀海水。
二〇一一年我在做单体版进销存时,单表最多二十一万行,给 status 字段加个普通索引,`WHERE status='NEW'` 平均零点几毫秒就出结果,那时候 MySQL 5.5 的 innodb_buffer_pool 才给了八百兆,照样跑得飞起。
现在这套系统 order 表带 stat_ctime 联合索引,数据八百多万,每次范围捞 INIT 状态跟趟沼泽一样。

文章配图,仅供参考

索引 cardinality 低得简直羞辱人。
我前天下午四点零二分用 `SHOW INDEX FROM ord_mast;` 看基数,stat 字段只有五个 distinct 值,idx_stat_ctime 的 Cardinality 显示为一百四十万,可实际 DISTINCT stat 才五。优化器选它只是因为没别的了。
一九年我维护 CRM 时遇到过类似坎,那时我用 force index(create_time) 加程序侧按天分片硬拆,慢查从两秒压到一百八十毫秒。
这次我干脆在 DAO 层把 stat='INIT' 改成按 ctime 倒序走主键扫描,配合 `innodb_change_buffering=all` 和把 `tmp121_tb` 这个遗留临时表迁移走,平均响应回到两百六十毫秒上下。
B+树在低选择性字段上就是毒瘤,堪称慢性自杀式设计。
昨天周二上午十点碰评审,王工翻着建表脚本说,"别再给状态字段建头一列索引了,我们上次上线 MySQL 8.0.32 后忘了删 idx_flag,导致 insert 带二级索引维护多花零点四毫秒,两百并发下写堆积到 socket timeout 返回 502"。他说这话时在白板上画了个歪歪扭扭的树,叶子节点全涂黑。
我用 sysbench 在测试库回放 reproduce 了,写场景返回的 error 码里有 1205 lock wait timeout 和一堆 502,平均延迟 2.3 秒,TPS 从一千三掉到两百八。
范围查找 löschen 掉那个联合索引之后,世界安静了。
我昨天夜里十一点半登到跳板机,执行 `ALTER TABLE ord_mast DROP INDEX idx_stat_ctime;`,随后 tail -f slow.log,看到那条语句不再出现,取而代之是一条走 PRIMARY 的相近行数扫描在五十八毫秒收工。
一味相信 B+树万能的人绝对没被 cardinality 打过脸。

(编辑:站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!