为什么关系型数据库无法全文检索?

关系型数据库通常以 B+ 树作为底层索引。一般我们在 MySQL 中模糊搜索这样用:

1
select * from my_table where title like '%term%';

首先这个查询一定会触发全表扫描,即使 title 字段上建立了索引,like 语句中的 % 也会导致索引生效。

全表扫描有多慢?

全表扫描本质上就是要把整张表的数据从磁盘中顺序读取出来,读取速度取决于存储介质的 I/O,因此耗时与数据量强相关,数据量越大,扫描时间越久。

相关性排序

除了慢,传统数据库无法计算查询词与存储字段的相关性,数据库只保证查的出来,不保证查的相关性够强。

倒排索引

搜索引擎利用倒排索引解决上述问题。

倒排索引的本质上就是:term -> posting list 的映射。

如何构建倒排索引呢?

分词 + 索引

分词

搜索引擎中提供分词器 Analyzer,用于数据在写入倒排或者查询倒排时将内容切分成 term,成为倒排索引中的 key:term。

eg:有如下文本:小米手机, 为发烧而生,经过 Analyzer 处理后会得到:

小米/手机/为/发烧/而生

常用的中文分词器有:ik,jieba 等。

一般都有以下几部分组成:

  1. 首先对文本进行预处理:字符过滤,去除特殊字符;
  2. 其次根据分词词典切分 term;
  3. 最后进行大小写转换、根据停用词词典去除停用词、根据相似词词典扩展同义词、近似词。
索引

根据切分的 term 和对应的 doc 构建倒排索引,比如:

1
2
3
term: 小米 -> posting list: doc(小米手机, 为发烧而生)
term: 手机 -> posting list: doc(小米手机, 为发烧而生)
term: 发烧 -> posting list: doc(小米手机, 为发烧而生)

那么在搜索 小米手机 的时候,关键词 小米手机 被拆分为 小米手机 ,然后搜索引擎读取两个 term 的倒排列表,取出 term 对应的 doc 合并就得到相关的结果。

ES 中的倒排索引

ElasticSearch 使用 Lucene 全文检索库构建倒排索引。

在 Lucene 中:

  • term 设计为 FST(有限状态转换器)的数据结构;
  • posting list 通过 FOR 和 位图等压缩技术,压缩比极高。

Reference

https://tech.meituan.com/2022/11/17/elasicsearch-optimization-practice-based-on-run-length-encoding.html