MySQL原生LIKE '%关键词%'在千万级表中不可用,因B+树索引失效需全表扫描;中文任意位置模糊搜索应换用Elasticsearch,并配ik分词器、binlog实时同步及match_phrase等合理DSL。

MySQL LIKE 在千万级表里慢得像卡住,别硬扛
直接说结论:原生 LIKE '%关键词%' 在大表上基本不可用,B+ 树索引完全失效,只能全表扫描。哪怕加了 name 字段的普通索引,只要左边带通配符,就等于没建。
常见错误现象:EXPLAIN 显示 type=ALL、rows 接近总行数、查询耗时从毫秒跳到秒级甚至分钟级。
- 真正能走索引的只有
LIKE '前缀%'(例如LIKE '张%'),这时可用 B+ 树快速定位 -
LIKE '%张%'或LIKE '%张'都无法利用常规索引,MySQL 5.7+ 的全文索引(FULLTEXT)只支持英文分词,中文效果极差 - 如果业务真要“任意位置匹配中文”,MySQL 不是合适工具——这不是调优问题,是能力边界问题
Elasticsearch 同步数据不是“导一次就完事”
把 MySQL 表 dump 进 ES 只是起点,后续增删改不实时同步,查出来的就是过期数据。最常踩的坑是:用脚本定时全量 reindex,结果中间十分钟查不到新订单、改价也不生效。
使用场景决定同步方案:
- 变更频率低(如商品类目)、容忍分钟级延迟 → 用 Logstash + JDBC Input,配合
schedule定时拉取updated_at > :sql_last_value - 要求秒级一致(如用户搜索订单)→ 必须捕获 MySQL binlog,推荐
canal或debezium,解析后发到 Kafka,再由消费者写入 ES - 千万别用应用层双写(代码里同时更新 MySQL 和 ES):失败回滚难、时序难保证、事务不一致风险高
ES 全文索引字段必须显式定义 analyzer
直接 PUT /my_index 然后塞中文进去,搜“笔记本”可能查不到“笔记本电脑”,因为默认 standard 分词器把中文按字切(“笔”、“记”、“本”),而不是按词(“笔记本”、“笔记本电脑”)。
实操建议:
- 中文字段必须指定
ik_max_word或jieba等中文分词器,例如 mapping 中写:"title": { "type": "text", "analyzer": "ik_max_word" } - 如果既要精确匹配又要模糊搜索,同一个字段可设多字段:
"title.keyword"用于 term 查询,"title"用于 match 查询 - 注意
analyzer只在索引时生效;搜索时若未指定search_analyzer,会默认复用analyzer,导致搜“python教程”被拆成“python”“教程”,漏掉“Python 教程”这种大小写混合写法
模糊查询别乱用 wildcard 和 regexp
wildcard 查询(如 {"wildcard": {"title": "*笔记本*"}})看着像 SQL 的 LIKE,但底层是遍历所有倒排索引项,数据量大时比 match_phrase 慢一个数量级,还容易触发 circuit_breaking_exception。
更安全的替代方案:
- 前缀搜索用
prefix查询(如{"prefix": {"title": "笔记本"}}),只查以该字符串开头的文档,性能接近 term 查询 - 想支持错别字或拼音简写,用
fuzzy查询({"fuzzy": {"title": "笔计本"}}),但fuzziness别设太高,否则爆炸式膨胀候选词 - 实在要跨词匹配,优先用
match_phrase+slop(允许中间隔几个词),比regexp稳定得多;regexp在大数据集上可能 OOM
复杂点在于:分词器选择、同步链路健壮性、查询 DSL 写法三者必须咬合。少一个环节,搜索就变成玄学——不是 ES 慢,是它根本没拿到该有的数据,或者拿到了却不会切词。










