FTS5 全文检索
本教程共 50 篇 · 第 43 篇 · 更新于 2026-07-31
43. FTS5 全文检索
本节目标:学完本章你能创建 FTS5 虚拟表、用 MATCH 做全文搜索,并理解它和普通 LIKE 模糊匹配的本质区别。
前面第 23 章我们学过用 LIKE 做模糊匹配,第 42 章也提过 LIKE '张%' 还能蹭一蹭索引。但如果你要在一篇几千字的长文章里搜”数据库优化”,或者想搜”sqlite OR 索引”这种带逻辑关系、还支持按相关度排序的需求,LIKE 就力不从心了:它只能做简单的子串匹配,遇到”词”的概念、同义词、相关度排序就抓瞎,而且以 % 开头时索引完全失效、只能全表扫。
SQLite 给出的专业方案是全文检索(Full-Text Search,简称 FTS)。当前官方推荐、也是现行版本内置的是 FTS5(FTS1/FTS2 已废弃,FTS3/FTS4 为旧版;新项目一律用 FTS5)。
为什么需要专门的 FTS
打个比方:LIKE 搜长文,相当于让你在一本书里找所有含”数据库”这三个连字的句子——你得逐字扫描。LIKE '%数据库%' 更惨,连索引都用不上。而 FTS 更像书的”关键词索引”:它提前把每篇文档拆成一个个”词”(术语叫 token,分词结果),建成倒排索引,搜的时候直接定位到包含这些词的文档,还能告诉你”哪篇出现得更多、更相关”。
正因为 SQLite 是嵌入式、零配置的(没有独立服务进程,数据库就是一个文件),它把全文检索也做成了”虚拟表”(Virtual Table)模块,开箱即用,不需要额外装搜索引擎服务。
创建 FTS5 虚拟表
FTS 表用 CREATE VIRTUAL TABLE ... USING fts5(...) 创建。注意它和普通表的建法不同,关键字是 VIRTUAL TABLE 和模块名 fts5:
CREATE VIRTUAL TABLE articles USING fts5(
title,
body
);
这样就建好了一个有两个可搜索列(title、body)的全文检索表。插入数据的方式和普通表一样:
INSERT INTO articles(title, body) VALUES
('SQLite 入门', 'SQLite 是一个轻量的嵌入式数据库,适合单机与边缘场景'),
('索引优化', '为高频查询建索引可以避免全表扫描,提升查询速度'),
('事务与并发', 'BEGIN 和 COMMIT 保证一组操作原子执行');
重点提示
FTS5 表里所有你定义的列,存入时都会被统一转成 TEXT 存储,类型亲和性在这里不起常规作用——这正是 SQLite 动态类型”按值决定存储”的一个特例体现。FTS5 还自动带一个隐藏列(和表同名),专门用于 MATCH 的左操作数。
用 MATCH 做全文查询
FTS 不使用 LIKE,而是专用的 MATCH 操作符:
sqlite> SELECT * FROM articles WHERE articles MATCH '索引';
这会返回所有 title 或 body 里包含”索引”这个词的行。也可以只在某一列里搜:
SELECT * FROM articles WHERE body MATCH '事务';
想跨所有列搜、又想限定某列时,可以用 列名: 前缀语法:
SELECT * FROM articles WHERE articles MATCH 'title:SQLite';
实用技巧
FTS5 的 MATCH 查询默认对英文是大小写不敏感的;对中文,能否正确”分词”取决于你使用的分词器(tokenizer)。下面会讲
trigram这种对中文友好的选项。
进阶:布尔、短语与相关度排序
FTS5 的 MATCH 语法相当强大,支持类搜索引擎的逻辑:
- 多词默认是”与”关系:
MATCH 'sqlite 索引'表示同时含这两个词。 - OR 连接:
MATCH 'sqlite OR 事务'表示含任意一个即可。 - 短语(加引号):
MATCH '"嵌入式 数据库"'要求词按此顺序相邻出现。 - 前缀(加星号):
MATCH '数据*'匹配以”数据”开头的词。
更妙的是 FTS5 提供了 bm25() 辅助函数,用来按”相关度”从高到低排序——出现关键词越多、越集中的文档得分越高:
SELECT title, bm25(articles) AS score
FROM articles
WHERE articles MATCH '索引'
ORDER BY score;
bm25() 返回的是负数,值越小(越负)代表越相关,所以直接 ORDER BY score 即可把最相关的排前面。这是 LIKE 永远给不了你的能力。
其实 FTS5 还内置了一个更省事的 rank 特殊列,它等价于 bm25() 的默认值,排序时直接 ORDER BY rank 即可,不必手动写函数:
SELECT title, rank
FROM articles
WHERE articles MATCH '索引'
ORDER BY rank;
这样写更简洁,也是官方推荐的相关度排序写法。需要的话你也能用 ORDER BY rank DESC 反向排——具体看你是想”最相关在前”还是”最不相关在前”。
重点提示
rank只是bm25()的语法糖,默认就按 bm25 算法算相关度。如果你的搜索需要”标题命中比正文命中更重要”这类偏好,可以给 bm25 传权重参数,例如bm25(articles, 10.0, 1.0)表示给第一列(title)更高权重。
分词器:中文搜索的关键
分词器(tokenizer)决定”怎么把一段文本拆成词”。FTS5 默认的 unicode61 分词器按空白和标点切分,对英文友好,但对中文这种”不分词空格”的语言,往往会把整句当一个 token,导致搜单个中文词搜不到。
解决中文全文检索,常用 FTS5 内置的 trigram(三元组)分词器——它把文本按连续的三个字符切片建索引,能很好地支持中文子串匹配:
CREATE VIRTUAL TABLE notes USING fts5(
content,
tokenize = 'trigram'
);
INSERT INTO notes(content) VALUES ('在嵌入式数据库里做全文检索很方便');
SELECT * FROM notes WHERE notes MATCH '嵌入式数据库';
只要 MATCH 的内容长度 ≥ 3 个字符,trigram 就能命中。这对中文场景是最省心的选择。
常见坑
别用普通
LIKE的写法去套 FTS。FTS 表里搜内容必须用MATCH,写WHERE body LIKE '%...%'不仅浪费 FTS 索引,还会退化成全表扫描。另外 FTS5 是现行版本;若看到资料写USING fts3或USING fts4,那是用旧模块,新项目请以 FTS5 为准。
删除、更新与维护
FTS5 虚拟表支持普通的 INSERT/DELETE/UPDATE,底层索引会自动维护。如果想彻底重建索引(比如导入大量数据后),可以:
INSERT INTO articles(articles) VALUES('rebuild');
这一行特殊的”命令式”插入会触发 FTS5 重建整张表的全文索引,在批量写入后用来整理很有用。
需要删表时和常规表一样用 DROP TABLE articles;,FTS5 相关的影子表会一并清理。
适用场景与边界
FTS5 适合:站内文章搜索、日志关键字检索、笔记/文档库的”搜词”功能、聊天记录查找等”在大量文本里找词”的场景。
它不适合:精确的数值/日期过滤(那应该建普通索引或用 WHERE 条件)、需要跨表关联的分析查询。fts 表本质是”为搜索而生”的特化结构。
重点提示
FTS5 与第 42 章的 B 树索引是互补关系:B 树索引擅长”等值/范围/排序”的精准定位,FTS5 擅长”文档里找词 + 相关度排序”。实际项目里常常两者并存——用普通列存结构化字段(如
created_at用 TEXT 存 ISO8601 文本),用 FTS5 表存正文做搜索。
类比小结
把 FTS5 想象成”给文档建关键词卡片柜”:
CREATE VIRTUAL TABLE ... USING fts5= 立一个带抽屉的卡片柜;- 分词器 = 决定按”英文单词”还是”中文三字串”来抄卡片;
MATCH '词'= 直接翻卡片找到对应文档,而不是通读每本书;bm25()= 给每篇文档算个”相关度分数”,帮你把最贴切的排最前。
这样你就不必再靠 LIKE 全表硬扫,搜索体验直接质的飞跃。下一章我们换个话题——数据库用久了文件会”虚胖”,该怎么用 VACUUM 给它瘦身整理。