国产数据库全文检索优化实录:告别 LIKE %关键词% 的慢查询之痛
一条 SQL 拖垮整个搜索体验
知识库服务的文档搜索功能,上线半年后开始频繁触发慢查询告警。定位下来,罪魁祸首是一行 MyBatis Mapper 里的模糊查询:
<select id="searchDocuments" resultType="KnowledgeDocument">
SELECT * FROM knowledge_document
WHERE doc_name LIKE CONCAT('%', #{keyword}, '%')
AND status = 1
ORDER BY update_time DESC
</select>
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)
达梦数据库内置了全文检索引擎,支持两种索引类型:
对于 doc_name 这种短文本字段,CTXCAT 更合适:
-- 创建全文索引
CREATE FULLTEXT INDEX idx_doc_name_ft
ON knowledge_document(doc_name)
LEXER 'CHINESE_LEXER';
-- 查询改写
SELECT * FROM knowledge_document
WHERE CONTAINS(doc_name, #{keyword})
AND status = 1
ORDER BY update_time DESC;
实测结果:50 万文档,关键词查询 P99 从 3200ms 降到 15ms,提升约 200 倍。
注意事项:
方案二:反向函数索引
这是一个巧妙的取巧方案。LIKE 'xxx%' 是可以用索引的(前缀匹配),那把字符串反转过来存,后缀匹配就变成了前缀匹配:
-- 创建反向函数索引
CREATE INDEX idx_doc_name_reverse
ON knowledge_document(REVERSE(doc_name));
-- 查询改写:搜 "报告" → 反转为 "告报" → 前缀匹配
SELECT * FROM knowledge_document
WHERE REVERSE(doc_name) LIKE CONCAT(REVERSE(#{keyword}), '%')
AND status = 1
ORDER BY update_time DESC;
实测结果:P99 从 3200ms 降到 180ms,提升约 17 倍。
局限性:
方案三:维持 LIKE + 限定范围
如果不想改索引结构,还有一个折中方案:在 LIKE 基础上增加限定条件,减少扫描范围。
SELECT * FROM knowledge_document
WHERE doc_name LIKE CONCAT('%', #{keyword}, '%')
AND status = 1
AND update_time >= DATEADD(MONTH, -6, CURRENT_TIMESTAMP)
ORDER BY update_time DESC
LIMIT 50;
通过时间范围 + 分页限制,把扫描行数从 50 万压缩到几万。实测 P99 约 800ms,能接受但不理想。
三种方案对比
| 维度 | 全文索引 (CTXCAT) | 反向函数索引 | LIKE + 限定范围 |
|------|:---:|:---:|:---:|
| 查询性能 (P99) | 15ms | 180ms | 800ms |
| 改造成本 | 中(需改 SQL + 建索引 + 同步机制) | 低(加索引 + 改 SQL) | 极低(只改 SQL) |
| 存储开销 | 高(+30%~50%) | 中(一列索引) | 无 |
| 写入影响 | 中(需异步同步) | 低(函数索引维护) | 无 |
| 搜索质量 | 高(支持分词、相关度排序) | 低(精确子串) | 低(精确子串) |
| 适用数据量 | 百万级以上 | 十万~百万 | 十万以内 |
迁移计划与回滚策略
我们选择了全文索引方案,分三个阶段上线:
第一阶段:并行部署(1 周)
第二阶段:灰度切流(1 周)
第三阶段:清理旧逻辑(1 周后)
回滚方案:配置开关一键切回 LIKE 查询,全文索引可以异步删除,不影响线上服务。整个过程对用户透明。
什么时候该上 Elasticsearch?
达梦的全文索引能解决 80% 的搜索优化需求,但以下场景建议直接引入 ES:
全文索引是数据库内置能力的天花板,ES 是专业搜索引擎的起点。如果你的搜索需求已经超出了"能搜到"的范畴,开始追求"搜得准、搜得快、搜得智能",那就别在数据库里折腾了,老老实实搭一套 ES 集群。
对于当前这个知识库场景,50 万文档 + 标题模糊搜索,达梦全文索引已经够用。先解决眼前的性能问题,等需求演进到复杂搜索再考虑架构升级,这才是务实的做法。
原文链接:https://wenyiblog.top/2026/06/dameng-fulltext-search-optimization/
首发于文艺技术笔记(wenyiblog.top),转载请注明出处。

浙公网安备 33010602011771号