From c56c06555e931ab217617f46f02b515ac0bb610b Mon Sep 17 00:00:00 2001 From: asahi Date: Wed, 19 Feb 2025 20:47:10 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=85=E8=AF=BB=E5=85=A8=E6=96=87=E7=B4=A2?= =?UTF-8?q?=E5=BC=95=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mysql/mysql文档/mysql_索引.md | 216 ++++++++++++++++++++++++++++++ 1 file changed, 216 insertions(+) diff --git a/mysql/mysql文档/mysql_索引.md b/mysql/mysql文档/mysql_索引.md index 9c685b3..504124a 100644 --- a/mysql/mysql文档/mysql_索引.md +++ b/mysql/mysql文档/mysql_索引.md @@ -34,6 +34,28 @@ - [Index Condition Pushdown(ICP)](#index-condition-pushdownicp) - [关闭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索引与算法 @@ -377,3 +399,197 @@ select * from people where zipcode = '95054' and last_name like '%asahi%' and ad ### 开启ICP 当开启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中。 + +