阅读全文索引文档

This commit is contained in:
asahi
2025-02-19 20:47:10 +08:00
parent 3c65a5f827
commit c56c06555e

View File

@@ -34,6 +34,28 @@
- [Index Condition Pushdown(ICP)](#index-condition-pushdownicp) - [Index Condition Pushdown(ICP)](#index-condition-pushdownicp)
- [关闭ICP](#关闭icp) - [关闭ICP](#关闭icp)
- [开启ICP](#开启icp) - [开启ICP](#开启icp)
- [innodb hash](#innodb-hash)
- [innodb中的哈希](#innodb中的哈希)
- [自适应hash索引](#自适应hash索引)
- [全文检索](#全文检索)
- [倒排索引](#倒排索引)
- [inverted file index](#inverted-file-index)
- [full inverted index](#full-inverted-index)
- [innodb全文检索](#innodb全文检索)
- [辅助表](#辅助表)
- [FTS Index Cache (全文检索索引缓存)](#fts-index-cache-全文检索索引缓存)
- [查看指定倒排索引的辅助表分词信息](#查看指定倒排索引的辅助表分词信息)
- [FTS Index Cache更新和写盘时机](#fts-index-cache更新和写盘时机)
- [分词删除](#分词删除)
- [OPTIMIZE TABLE](#optimize-table)
- [innodb\_ft\_cache\_size](#innodb_ft_cache_size)
- [FTS Document id](#fts-document-id)
- [innodb全文检索示例](#innodb全文检索示例)
- [innodb full-text design](#innodb-full-text-design)
- [innodb full-text index tables](#innodb-full-text-index-tables)
- [辅助索引表](#辅助索引表)
- [table\_id hex](#table_id-hex)
- [index\_id hex](#index_id-hex)
# innodb索引与算法 # innodb索引与算法
@@ -377,3 +399,197 @@ select * from people where zipcode = '95054' and last_name like '%asahi%' and ad
### 开启ICP ### 开启ICP
当开启ICP后数据库会将where的部分过滤条件放在存储引擎层在索引获取数据同时就会进行where条件的过滤。 当开启ICP后数据库会将where的部分过滤条件放在存储引擎层在索引获取数据同时就会进行where条件的过滤。
## innodb hash
在innodb中采用除法散列的哈希方法通过`k % m`将关键字`k`映射到`m`个槽中的一个,即
```
hash(k) = k % m
```
### innodb中的哈希
innodb存储引擎采用哈希算法对字典进行查找冲突机制采用链表方式哈希函数采用`k % m`的方式进行散列。
对于缓冲池页的哈希表缓冲池中的page页都有一个chain指针指向相同哈希值的页。而对于`m`的取值,其规则如下:
- m的取值应略大于2倍的缓冲池页数量的质数
- 例如,若`innodb_buffer_pool_size`大小为10M那么缓冲池页数量为 `10 * 1024 / 16 = 640`可容纳240个缓冲页故而对于缓冲池页内存的哈希表来说需要分配的`槽个数m`为大于`640 * 2`的质数,即`1399`
### 自适应hash索引
自适应hash索引可以通过参数`innodb_adaptive_hash_index`来进行开启或关闭并且自适应hash索引的使用情况可以通过`show engine innodb status`来进行查看:
```
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
0.00 hash searches/s, 320660.58 non-hash searches/s
```
可以根据`hash searches/s``non-hash searches/s`来查看自适应hash索引的使用情况。
## 全文检索
### 倒排索引
全文检索通常使用`倒排索引`(inverted index)来实现和B+树索引一致,倒排索引也是一种索引结构。
倒排索引在辅助表(auxiliary table)中存储了`单词``单词自身在一个或多个文档中所在位置`之间的映射关系。通常,倒排索引通过关联数组可以实现,拥有两种表现形式:
- inverted file index: {单词单词所在文档id}
- full inverted index: {单词, (单词所在文档id, 在文档中的具体位置)}
#### inverted file index
对于inverted file index其存储的数据结构如下所示
| Number | Text | Documents |
| :-: | :-: | :-: |
| 1 | code | 1,4 |
| 2 | days | 3,6 |
| 3 | hot | 1,4 |
其中,`Documents`存储的是包含查询关键字的文档id数组。
> 对于inverted file index其仅保存文档的id
#### full inverted index
对于full inverted index其存储的是`(文档id文档中位置)``pair`
full inverted index的存储结构示例如下所示;
| Number | Text |Documents |
| :-: | :-: | :-: |
| 1 | code | (1:6), (4:8) |
| 2 | days | (3:2), (6:2) |
| 3 | hot | (1:3), (4:4) |
> full inverted index 相较于 inverted file index除了存储文档id之外还存储单词所在文档的位置信息。
### innodb全文检索
innodb支持full inverted index形式的全文检索。
在innodb中`(DocumentId, Position)`视为`ilist`。故而,在全文检索的表中,存在两个字段,`word``ilist`,并在`word`字段设有索引。`并且innodb在ilist中存放了position信息故而可以支持proximity search`
#### 辅助表
innodb中倒排索引需要将`word`存放在辅助表中,并且,为了提高全文检索的并行能力,共存在`6`张辅助表每张表根据word的Latin进行分区。
> 辅助表为持久化的表,存放在磁盘中。
#### FTS Index Cache (全文检索索引缓存)
FTS Index Cache为红黑树结构根据`(word, ilist)`进行排序。
在插入数据时,即使插入数据已经更新了对应的数据库表,但是对`全文索引`更新可能仍位于`FTS Index Cache`即辅助表尚未被更新。Innodb会批量对辅助表进行更新而非每次插入后都立即更新索引表。
当对全文检索进行查询时辅助表首先会将FTS Index Cache中对应word字段合并到辅助表中然后再在辅助表中执行查询操作。
> 上述FTS Index Cache操作类似Insert Buffer其能提高innodb的性能并且其由红黑树排序后再执行批量插入其产生的辅助表相对较小。
#### 查看指定倒排索引的辅助表分词信息
innodb允许用户查看指定倒排索引辅助表的分词信息可以通过设置`innodb_ft_aux_table`来查看倒排索引的辅助表。
```sql
set global innodb_ft_aux_table='{schema_name}/{table_name}';
```
执行完上述语句后,可以在`information_schema.innodb_ft_index_table`中查询表的分词信息。
#### FTS Index Cache更新和写盘时机
Innodb在事务提交时将分词写入到`FTS Index Cache`中,然后再根据批量更新将`FTS Index Cache`写入到磁盘。
在事务提交时FTS Index Cache中数据会同步到磁盘的辅助表中。但是当数据库发生宕机时FTS Index Search中的数据可能尚未被同步到磁盘中。
在上述宕机情况下下次数据库重启时若用户对表进行全文检索操作插入或查询innodb会自动读取未完成的文档并然后进行分词操作再次将分词结果放入到FTS Index Cache中。
#### 分词删除
对于文档中分词的删除操作,在事务提交时,不删除辅助表中的数据,而只是删除`FTS Index Cache`中的记录。对于被删除的记录会根记录其FTS Dcoument Id并且将该id保存在`deleted辅助表`中。
> 在删除文档时,文档内容从一般表中被删除,但是`索引辅助表`中的数据并不会被删除,相反的,还会将`FTS_DOC_ID`添加到`deleted辅助表`中。
#### OPTIMIZE TABLE
由上述内容可知对文档的DML并不会实际删除`索引中的数据`,只是会在`deleted辅助表`中添加`FTS_DOC_ID`,故而在应用程序运行时,`索引会变得越来越大`
mysql允许通过`optimize table`命令来实际将已删除记录从索引中删除。
由于`optimize table`语句不仅会删除索引中的数据,还会执行其他操作,例如`Cardinality`重新统计等。
> 如果用户仅希望对倒排索引进行操作,可以设置`innodb_optimize_fulltext_only`参数。
>
> 可以执行如下语句
> ```sql
> set global innodb_optimize_fulltext_only=1;
> optimize table xxx;
> ```
如果被删除文档很多那么optimize table可能会花费大量时间这将对程序的运行造成影响。
用户可以通过参数`innodb_ft_num_word_optimize`来限制每次实际删除的分词数量该参数默认值为2000.
#### innodb_ft_cache_size
可以通过`innodb_ft_cache_size`来控制FTS Index Cache的大小默认大小为`32M`。当缓冲被填满后,会将缓存中`(word, ilist)`数据同步到位于磁盘的辅助表中。
适当增加`innodb_ft_cache_size`参数的值能够提升全文检索的性能,但是在宕机时,位同步到磁盘中的数据可能需要更长时间来进行恢复。
#### FTS Document id
在innodb存储引擎中为了支持全文检索必须存在一个字段和辅助表中的`word`进行对应。在innodb中对应字段为`FTS_DOC_ID`
`FST_DOC_ID`字段的字段类型必须为`BIGINT UNSIGNED NOT NULL`并且innodb存储引擎会自动为该列添加名为`FST_DOC_ID_INDEX`的唯一索引`unique index`
### innodb全文检索示例
full-text index基于`text based columns`char, varchar, text来进行创建用于加速针对`text based colums`列的dml操作。
一个full-text index可以定义为`create table`语句的一部分,也可以通过`alter table``create index`语句添加到已经存在的表中。
full-text查询通过`match() ... against`预发来触发。
#### innodb full-text design
innodb全文索引基于倒排索引进行设计。倒排索引存储了一系列的words对每个word都存在一个list与之对应list中的元素为`包含word的文档`
为了支持`proximity search`word在list中出现的位置信息也同样被保存。
#### innodb full-text index tables
当innodb full-text index被创建时一系列index tables都会同时被创建示例如下所示。
创建表并指定fulltext索引
```sql
create table fs_text (
id bigint not null auto_increment,
content longtext,
primary key (id),
fulltext index `idx_fts_fs_text` (content)
);
```
查询辅助表:
```sql
select * from information_schema.innodb_tables where name like 'innodb_demo%'
```
其中和full-text表相关的索引如下
| TABLE\_ID | NAME | FLAG | N\_COLS | SPACE | ROW\_FORMAT | ZIP\_PAGE\_SIZE | SPACE\_TYPE | INSTANT\_COLS | TOTAL\_ROW\_VERSIONS |
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
| 1822 | innodb\_demo/fs\_text | 33 | 6 | 96 | Dynamic | 0 | Single | 0 | 0 |
| 1823 | innodb\_demo/fts\_000000000000071e\_being\_deleted | 33 | 4 | 97 | Dynamic | 0 | Single | 0 | 0 |
| 1824 | innodb\_demo/fts\_000000000000071e\_being\_deleted\_cache | 33 | 4 | 98 | Dynamic | 0 | Single | 0 | 0 |
| 1825 | innodb\_demo/fts\_000000000000071e\_config | 33 | 5 | 99 | Dynamic | 0 | Single | 0 | 0 |
| 1826 | innodb\_demo/fts\_000000000000071e\_deleted | 33 | 4 | 100 | Dynamic | 0 | Single | 0 | 0 |
| 1827 | innodb\_demo/fts\_000000000000071e\_deleted\_cache | 33 | 4 | 101 | Dynamic | 0 | Single | 0 | 0 |
| 1828 | innodb\_demo/fts\_000000000000071e\_00000000000005ad\_index\_1 | 33 | 8 | 102 | Dynamic | 0 | Single | 0 | 0 |
| 1829 | innodb\_demo/fts\_000000000000071e\_00000000000005ad\_index\_2 | 33 | 8 | 103 | Dynamic | 0 | Single | 0 | 0 |
| 1830 | innodb\_demo/fts\_000000000000071e\_00000000000005ad\_index\_3 | 33 | 8 | 104 | Dynamic | 0 | Single | 0 | 0 |
| 1831 | innodb\_demo/fts\_000000000000071e\_00000000000005ad\_index\_4 | 33 | 8 | 105 | Dynamic | 0 | Single | 0 | 0 |
| 1832 | innodb\_demo/fts\_000000000000071e\_00000000000005ad\_index\_5 | 33 | 8 | 106 | Dynamic | 0 | Single | 0 | 0 |
| 1833 | innodb\_demo/fts\_000000000000071e\_00000000000005ad\_index\_6 | 33 | 8 | 107 | Dynamic | 0 | Single | 0 | 0 |
#### 辅助索引表
其中,以`innodb_demo/fts_000000000000071e_00000000000005ad_index`开头的6张表用于存储倒排索引并被称为`辅助索引表`
当新增的文档被分割为`token`时,每个独立的`word`(也可被称为`token`被插入到辅助索引表中随着word被插入的还有`postion``DOC_ID`
word按照`第一个字符的字符集排序权重`被排序,并且在六张辅助索引表中进行分区。
> 倒排索引被分区在6张辅助索引表中用于支持索引的并行创建。默认情况下2个线程执行`tokenize`, `sort`,`将word和关联数据插入到index tables`操作。
>
> 如果想要指定操作上述流程的线程数量,可以对`innodb_ft_sort_pll_degree`参数进行配置。如果要在大表上创建full-text index可以考虑增加该参数。
##### table_id hex
辅助索引表命名通过`fts_`开头,并且后缀`index_#`。每个辅助索引表都通过`辅助索引表表名中16进制的值`来和`被索引表的table id`来进行关联。例如上述示例中16进制值`071e`代表十进制`1822`,而表`fs_text``table_id`刚好为`1822`
##### index_id hex
在6张辅助索引表中除了包含`table_id`的hex外下划线后还存在一个16进制数部分该部分为`05ad`,代表十进制`1453`,而索引`idx_fts_fs_text``index_id`刚好为`1453`
> 如果是`file per table tablespace`那么idnex table将会保存在其自己的tablespace中。