线上系统报警那会儿,慢查询日志里刷出来的 SQL 长这样:WHERE order_no LIKE '%3109' ORDER BY created_at DESC LIMIT 20。表只有 310 万行,字段上也明明建了索引,可这条查询稳定跑在 2 秒以上。光看这条 SQL,大部分人的第一反应是“LIKE 就这德行,不以固定前缀开头,索引铁定失效”。这个判断没错,但要解决它,不是拆 SQL、改业务就能了事,得从存储侧想办法。最后我用的是标题里说的“反向存储大法”:多维护一列反转数据,把 %3109 这种后缀匹配改写成前缀匹配,查询直接从全表扫描变成索引范围扫描,同样条件下耗时降了两个数量级。这篇文章就把这个方案的原理、落地写法和使用边界完整拆一遍。
1. 线上这条慢 SQL,到底慢在哪个环节
1.1 业务场景和慢 SQL 长什么样
业务上有个搜索入口,用户只记得订单号的后几位。比如客户打电话来:“帮我查一下尾号 3109 的订单。”后端代码里很自然就会写成:
sql复制SELECT *
FROM trade_order
WHERE order_no LIKE '%3109'
ORDER BY created_at DESC
LIMIT 20;
这张 trade_order 表有 310 万行,order_no 上有一个普通二级索引。表结构大概是这样:
sql复制CREATE TABLE trade_order (
id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT,
order_no VARCHAR(32) NOT NULL,
user_id BIGINT UNSIGNED NOT NULL,
created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (id),
KEY idx_order_no (order_no)
) ENGINE=InnoDB;
在 30 万行的时候这条 SQL 可能没什么体感,但数据一过 300 万,赶上晚高峰,慢查询立刻变成告警。我拉出执行计划:
text复制id | sele
