一条 SQL 拖垮整个搜索体验
知识库服务的文档搜索功能,上线半年后开始频繁触发慢查询告警。定位下来,罪魁祸首是一行 MyBatis Mapper 里的模糊查询:
| |
LIKE '%关键词%' 这个写法,但凡写过 SQL 的人都知道它走不了 B-Tree 索引。但在数据量只有几千条的时候,全表扫描也就几十毫秒,没人当回事。等到文档数量突破 50 万,P99 响应时间直接飙到 3 秒以上。
现有架构与性能瓶颈
我们的环境是达梦数据库 8.0,知识库服务(knowledge-service)通过 MyBatis 访问。文档表 knowledge_document 的结构大致如下:
| 字段 | 类型 | 说明 |
|---|---|---|
| id | BIGINT | 主键 |
| doc_name | VARCHAR(500) | 文档名称 |
| doc_content | CLOB | 文档正文 |
| status | INT | 状态 |
| update_time | TIMESTAMP | 更新时间 |
问题链路很清晰:用户输入关键词 → MyBatis 拼出 LIKE '%xxx%' → 达梦优化器判定无法使用普通索引 → 全表扫描 50 万行 → 返回结果。
瓶颈不仅是单次查询慢。高峰期搜索请求并发上来,全表扫描把 Buffer Pool 里的热页全挤出去,连带其他业务查询也跟着变慢。一个搜索功能,变成了整个系统的性能黑洞。
方案一:全文索引(CTXCAT / CONTEXT)
达梦数据库内置了全文检索引擎,支持两种索引类型:
- CONTEXT:适合长文本(如文档正文),基于分词建立倒排索引,支持
CONTAINS()语法 - CTXCAT:适合短文本(如标题、名称),结合结构化字段做混合查询,支持
CATSEARCH()语法
对于 doc_name 这种短文本字段,CTXCAT 更合适:
| |
实测结果:50 万文档,关键词查询 P99 从 3200ms 降到 15ms,提升约 200 倍。
注意事项:
- 需要维护索引同步,DML 操作后索引不是实时更新的,可以通过定时任务或触发器做增量同步
- 中文分词器的选择直接影响搜索质量,达梦自带的
CHINESE_LEXER基础够用,但遇到行业术语可能需要自定义词典 - 全文索引会额外占用约原文数据量 30%~50% 的存储空间
方案二:反向函数索引
这是一个巧妙的取巧方案。LIKE 'xxx%' 是可以用索引的(前缀匹配),那把字符串反转过来存,后缀匹配就变成了前缀匹配:
| |
实测结果:P99 从 3200ms 降到 180ms,提升约 17 倍。
局限性:
- 只解决了"包含"查询中"后缀匹配"的部分场景,对于中间位置的子串匹配效果有限
- 每次查询需要对关键词做
REVERSE()转换,应用层需要适配 - 函数索引在达梦中的维护成本比普通索引高,写入性能有一定损耗
方案三:维持 LIKE + 限定范围
如果不想改索引结构,还有一个折中方案:在 LIKE 基础上增加限定条件,减少扫描范围。
| |
通过时间范围 + 分页限制,把扫描行数从 50 万压缩到几万。实测 P99 约 800ms,能接受但不理想。
三种方案对比
| 维度 | 全文索引 (CTXCAT) | 反向函数索引 | LIKE + 限定范围 |
|---|---|---|---|
| 查询性能 (P99) | 15ms | 180ms | 800ms |
| 改造成本 | 中(需改 SQL + 建索引 + 同步机制) | 低(加索引 + 改 SQL) | 极低(只改 SQL) |
| 存储开销 | 高(+30%~50%) | 中(一列索引) | 无 |
| 写入影响 | 中(需异步同步) | 低(函数索引维护) | 无 |
| 搜索质量 | 高(支持分词、相关度排序) | 低(精确子串) | 低(精确子串) |
| 适用数据量 | 百万级以上 | 十万~百万 | 十万以内 |
迁移计划与回滚策略
我们选择了全文索引方案,分三个阶段上线:
第一阶段:并行部署(1 周)
- 在测试环境建全文索引,跑回归测试验证搜索结果一致性
- 对比 LIKE 查询和 CONTAINS 查询的结果差异,重点验证中文分词效果
第二阶段:灰度切流(1 周)
- 通过配置开关控制走新查询还是旧查询
- 先在内部用户灰度 10%,观察搜索准确性和性能指标
- 确认无误后逐步放量到 100%
第三阶段:清理旧逻辑(1 周后)
- 移除 MyBatis 中的旧 LIKE 查询代码
- 配置全文索引定时同步任务(每 5 分钟增量同步)
回滚方案:配置开关一键切回 LIKE 查询,全文索引可以异步删除,不影响线上服务。整个过程对用户透明。
什么时候该上 Elasticsearch?
达梦的全文索引能解决 80% 的搜索优化需求,但以下场景建议直接引入 ES:
- 需要复杂的相关度排序(TF-IDF、BM25、自定义权重)
- 多字段联合搜索 + 高亮显示
- 搜索建议(Suggest)、拼写纠错、同义词扩展
- 数据量超过千万级,且对搜索延迟要求 < 50ms
- 需要跨多个异构数据源的统一搜索
全文索引是数据库内置能力的天花板,ES 是专业搜索引擎的起点。如果你的搜索需求已经超出了"能搜到"的范畴,开始追求"搜得准、搜得快、搜得智能",那就别在数据库里折腾了,老老实实搭一套 ES 集群。
对于当前这个知识库场景,50 万文档 + 标题模糊搜索,达梦全文索引已经够用。先解决眼前的性能问题,等需求演进到复杂搜索再考虑架构升级,这才是务实的做法。