首页 / SQLite 入门教程 / FTS5 全文检索

SQLite 入门教程

FTS5 全文检索

本教程共 50 篇 · 第 43 篇 · 更新于 2026-07-31

sqliteFTS5全文检索MATCH虚拟表

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
);

这样就建好了一个有两个可搜索列(titlebody)的全文检索表。插入数据的方式和普通表一样:

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 '索引';

这会返回所有 titlebody 里包含”索引”这个词的行。也可以只在某一列里搜:

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 fts3USING 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 给它瘦身整理。