InnoDB存储引擎笔记-索引与算法

Scroll Down

概述

索引是应用程序设计和开发的一个重要方面。若索引太多,应用程序的性能可能会收到影响。而索引太少,对查询性能又会产生影响。要找到一个合适的平衡点,这对应用程序的性能至关重要。

InnoDB存储引擎索引概述

InnoDB存储引擎支持以下几种常见的索引:

  • B+树索引
  • 全文索引
  • 哈希索引

前文已经提到过,InnoDB存储引擎支持的哈希索引是自适应的,InnoDB存储引擎会根据表的事情情况自动为表生成哈希索引,不能人为干预是否在一张表中生成哈希索引。B+树索引就是传统意义上的索引,这就是目前关系型数据库系统中查找最为常用和最为有效的索引。B+树索引的构造类似于二叉树,根据键值(Key value)快速找到数据。

注意 B+树中的B不是代表二叉(binary),而是代表平衡(balance),因为B+树是从最早的平衡二叉树演化而来的,但是B+树不是一个二叉树。

另外需要注意的是,B+树索引并不能找到一个给定键值的具体行。B+树索引能找到的只是被查找数据行所在的页。然后数据库通过把页读入到内存,再在内存中进行查找,最后得到要查找的数据。

数据结构与算法

B+树索引是最为常见,也是在数据库中使用最为频繁的一种索引。

二分查找法

二分查找法(binary search)也称为折半查找法,用来查找一组有序的记录数组中的某一记录,其基本思想是:将数据按有序化(递增或递减)排列,在查找过程中采用跳跃式方式查找,即先以有序数列的中点位置为比较对象,如果要找的元素值小于该中点元素,即将待查序列缩小为左半部分,否则为右半部分。通过一次比较,将查找区间缩小一半。
二分查找法应用极其广泛,而且它的思想易于理解。在前面的章节中,每页Page DIrectory中的槽是按照主键的顺序存放的,对于某一条具体记录的查询是通过对Page Directory进行二分查找得到的。

二叉查找树和平衡二叉树

在介绍B+树之前,需要先了解下二叉查找树。B+树是通过二叉查找树,再由平衡二叉树,B树演化来的。二叉查找树是一种经典的数据结构。
在二叉查找树中,左子树的键值总是小于根的键值,右子树的键值总是大于根的键值。因此可以通过中序遍历得到键值的排序输出。
平衡二叉树的定义如下:首先符合二叉查找树的定义,其次必须满足任何节点的两个子树的高度差最大差为1。

B+树

B+树和二叉树、平衡二叉树一样,都是经典的数据结构。B+树由B树和索引顺序访问方法演化而来,但是在现实使用过程中几乎已经没有使用B树的情况了。

B+树的插入操作

B+树的插入必须保证插入后叶子节点中的记录依然排序,同时需要考虑插入到B+树的三种情况,每种情况都可能会导致不同的插入算法。

B+树插入的3种情况
Leaf Page满 Index Page满 操作
No No 直接将记录插入到叶子节点
Yes No (1)拆分Leaf Page(2)将中间的节点放入到Index Page中(3)小于中间节点的记录放左边(4)大于或等于中间节点的记录放右边
Yes yes (1)拆分Leaf Page(2)小于中间节点的记录放左边(3)大于或等于中间节点的记录放右边(4)拆分Index Page(5)小于中间节点的记录放左边(6)大于或等于中间节点的记录放右边 (7)中间节点放入上一层Index Page
B+树同样提供了类似于平衡二叉树的旋转功能。
旋转发生在Leaf Page已经满,但是其左右兄弟没有满的情况下,这时B+树并不会急于去做拆分页操作,而是将记录移到所在页的兄弟节点上。在通常情况下,左兄弟会被首先检查用来做旋转操作。

B+树的删除操作

B+树使用填充因子(fill factor)来控制树的删除变化,50%是填充因子可设的最小值。B+树的删除操作同样必须保证删除后叶子节点中的记录依然排序,同插入一样,B+树的删除操作需要考虑以下三种情况,与插入不同的是,删除根据填充因子的变化来衡量。

B+树插入的3种情况
叶子节点小于填充因子 中间节点小于填充因子 操作
No No 直接将记录从叶子节点删除,如果该节点还是Index Page节点,用该节点的右节点代替
Yes No 合并叶子节点和它的兄弟节点,同时更新Index Page
Yes Yes (1)合并叶子节点和它的兄弟节点(2)更新Index Page (3)合并Index Page和它的兄弟节点

B+树索引

前面讨论的都是B+树的数据结构及其一般操作,B+树索引的本质就是B+树在数据库中的实现。但是B+树索引在数据库中有一个特点是高扇出性,因此在数据库中,B+树的高度一般都在2-4层,这也就是说查找某一键值的行记录时最多只需要2到4次IO,这倒不错,因为当前一般的机械磁盘每秒至少可以做100次IO,2-4次的IO意味着查询时间只需。0.02-0.04秒。
数据库中的B+树索引可以分为聚集索引(clustered index)和辅助索引(secondary index),但是不管是聚集还是辅助索引,其内部都是B+树的,即高度平衡的,叶子节点存放着所有的数据。聚集索引与辅助索引不同的是,叶子节点存放的是否是一整行的信息。

聚集索引

InnoDB存储引擎表是索引组织表,即表中数据按照主键顺序存放。而聚集索引(clustered index)就是按照每张表的主键构造一颗B+树,同时叶子节点中存放的即为整张表的行记录数据,也将聚集索引的叶子节点称为数据页。聚集索引的这个特性据定了索引组织表中数据也是索引的一部分。同B+树数据结构一样,每个数据页都通过一个双向链表来进行连接。
由于实际的数据页只能按照一颗B+树进行排序,因此每张表只能拥有一个聚集索引。在多数情况下,查询优化器倾向于采用聚集索引。因为聚集索引能够在B+树索引的叶子节点上直接找到数据。此外,由于定义了数据的逻辑顺序,聚集索引能够特别快的访问针对范围值的查询。查询优化器能够快速发现某一段范围的数据页需要扫描。
聚集索引的另一个好处是,它对于主键的顺序查找和范围查找速度非常快。叶子节点的数据就是用户所要查询的数据。如用户需要查询一张注册用户的表,查询最后注册的10位用户,由于B+树是双向链表的,用户可以快速找到最后一个数据页,并取出10条记录。
另一个是范围查询(range query)即如果要查找主键某一范围内的数据,通过叶子节点的上层中间节点就可以得到页的范围,之后直接读取数据页即可。

辅助索引

对于辅助索引(Secondary Index,也称非聚集索引),叶子节点并不包含行记录的全部数据。叶子节点除了包含键值以外,每个叶子节点中的索引行还包含了一个书签(bookmark)。该书签用来告诉InnoDB存储引擎哪里可以找到与索引相对应的行数据。由于InnoDB存储引擎表是索引组织表,因此InnoDB存储引擎的辅助索引的书签就是相应行数据的聚集索引键。
辅助索引的存在并不影响数据在聚集索引中的组织,因此每张表上可以有多个辅助索引。当通过辅助索引来寻找数据时,InnoDB存储引擎会遍历辅助索引并通过叶级别的指针获得指向主键索引的主键,然后在通过主键索引来找到一个完整的行记录。举例来说,如果在一棵高度为3的辅助索引中查找数据,那需要对这颗辅助索引树遍历3次找到执行主键,如果聚集索引树的高度同样为3,那么还需要对聚集索引树进行3次查找,最终找到一个完整的行数据所在的页,因此一共需要6次逻辑IO访问可以得到最终的一个数据页

B+树索引的分裂

B+树索引页的分别并不总是从页的中间记录开始,这样可能会导致页空间的浪费。InnoDB存储引擎的Page Header中有以下几个部分来保存插入的顺序信息:

  • PAGE_LAST_INSERT
  • PAGE_DIRECTION
  • PAGE_N_DIRECTION

通过这些信息,InnoDB存储引擎可以决定是向左还是向右进行分裂,同时决定将分裂点记录为哪一个。若插入是随机的,则取页的中间记录作为分裂点的记录,这和之前介绍的相同。若往同一方向进行插入的记录数量为5,并且目前已经定位到的记录之后还有3条记录,则分裂点的记录为定位到的记录后的第三条记录,否则分裂点记录就是待插入的记录。

Cardinality值

什么是Cardinality

并不是在所有的查询条件中出现的列都需要添加索引。对于什么时候添加B+树索引,一般的经验是,在访问表中很少一部分时使用B+树索引才有意义。对于性别字段、地区字段、类型字段、它们可取值的范围很小,称为低选择性。

SELECT * FROM student WHERE sex = 'M'

按性别进行查询时,可取值的范围一般只有’M’,‘F’。因此上述SQL语句得到的结果可能是该表50%的数据,这时添加B+树索引是完全没有必要的。相反,如果某个字段的取值范围很广,几乎没有重复,即属于高选择性,则此时使用B+树索引是最适合的。
怎样查看索引是否是高选择性呢?可以通过SHOW INDEX结果中列Cardinality来观察。Cardinality值非常关键,表示索引中不重复记录数量的预估值。同时需要注意的是,Cardinality是一个预估值,而不是一个准确值,在实际应用中,Cardinality/n_rows_in_table应尽可能地接近1,。如果非常小,那么用户考虑是否还有必要创建这个索引。故在访问高选择性属性的字段并从表中取出很少一部分数据时,对这个字段添加B+树索引是非常有必要的。

InnoDB存储引擎的Cardinality统计

在InnoDB存储引擎中,Cardinality统计信息的更新发生在两个操作中:INSERT和UPDATE。因此InnoDB存储引擎内部对更新Cardinality信息的策略为:

  • 表中1/16的数据已发生过变化。
  • stat_modified_counter > 2000000000。

第一种策略为自从上次统计Cardinality信息后,表中1/16的数据已经发生过变化,这时需要更新Cardinality信息。第二种情况考虑的是,如果对表中某一行数据频繁地进行更新操作,这时表中的数据实际并没有增加,实际发生变化的还是这一行数据,则第一种更新策略就无法适用这种情况。故在InnoDB存储引擎内部有一个计数器stat_modified_counter,用来表示发生变化的次数,当stat_modified_counter大于2000000000时,则同样需要更新Cardinality信息。
默认InnoDB存储引擎对8个叶子节点(Leaf Page)进行采用。采样的过程如下:

  • 取得B+树索引中叶子节点的数量,记为A。
  • 随机取得B+树索引中的8个叶子节点。统计每个页不同记录的个数,即为P1,P2…P8。
  • 根据采样信息给出Cardinality的预估值:Cardinality = (P1+P2+。。。+P8)* A/8。

InnoDB1.2版本之前,可以通过参数innodb_stats_sample_pages用来设置统计Cardinality时每次采样页的数量,默认值为8.同时参数innodb_stats_method用来判断如何对待索引中出现的NULL值记录。该参数默认值为nulls_equal,表示将NULL值记录视为相等的记录。其有效值还有nulls_unequal,nulls_ignored,分别表示将NULL值记录视为不同的记录和忽略NULL值记录。当执行SQL语句ANALYZE TABLE、SHOW TABLE STATUS、SHOW INDEX以及访问INFORMATION_SCHEMA架构下的表TABLES和STATISTICS时会导致InnoDB存储引擎去重新计算索引的Cardinality值。若表中的数据量非常大,并且表中存在多个辅助索引时,执行上述这些操作可能会非常慢。虽然用户可能并不希望去更新Cardinality的值。

B+树索引的使用

不同应用中B+树索引的使用

数据库中存在两种类型的应用,OLTP和OLAP应用。在OLTP应用中,查询操作只从数据库中取得一小部分数据,一般可能都在10条记录以下,甚至在很多时候只取1条记录,如根据主键值来取得用户信息,根据订单号取得订单的详细信息,这都是OLTP应用的查询语句。在这种情况下,B+树索引建立后,对该索引的使用应该只是通过该索引取得表中少部分的数据。这时建立B+树索引才是有意义的,否则即使建立了,优化器也可能选择不使用索引。
对于OLAP应用,情况可能就稍显复杂了。不过概括来说,在OLAP应用中,都需要访问表中大量的数据,根据这些数据来查询产生的查询结果,这些查询多是面向分析的查询,目的是为决策者提供支持。

联合索引

联合索引是指对表上的多个列进行索引。联合索引的创建方法与单个索引创建的方法一样,不同之处仅在于有多个索引列。那么何时需要使用联合索引呢?在讨论这个问题之前,先来看一下联合索引内部的结果。从本质上来说,联合索引也是一颗B+树,不同的是联合索引的键值的数量不是1,而是大于等于2。联合索引的第二个好处是已经对第二个键值进行了排序处理。
正如前面所介绍的那样,联合索引(a,b)其实是根据列a,b进行排序的,因此下列语句可以直接使用联合索引得到结果:

SELECT ... FROM TABLE WHERE a = xxx ORDER BY b

然而对于联合索引(a,b,c)来说,下列语句同样可以直接通过联合索引得到结果:

SELECT ... FROM TABLE WHERE a = xxx ORDER BY b
SELECT ... FROM TABLE WHERE a = xxx AND b = xxx ORDER BY c

但是对于下面的语句,联合索引不能直接得到结果,其还需要执行一次filesort排序操作,因为索引(a,c)并未排序:

SELECT ... FROM TABLE WHERE a = xxx ORDER BY c

覆盖索引

InnoDB存储引擎支持覆盖索引(covering index或称索引覆盖),即从辅助索引中就可以得到查询的记录,而不需要查询聚集索引中的记录。使用覆盖索引的一个好处是辅助索引不包含整行记录的所有信息,故其大小要远小于聚集索引,因此可以减少大量的IO操作。
对于InnoDB存储引擎的辅助索引而言,由于其包含了主键信息,因此其叶子节点存放的数据为(primary_key1, primary_key2, ···, key1, key2, ···)。例如,下列语句都可仅使用一次辅助联合索引来完成查询:

SELECT key2 FROM table WHERE key1 = xxx;
SELECT primary key2,key2 FROM table WHERE key1 = xxx;
SELECT primary key1,key2 FROM table WHERE key1 = xxx;
SELECT primary key1,primary key2,key2 FROM table WHERE key1 = xxx;

覆盖索引的另一个好处是对某些统计问题而言的。还是对于上一节创建的表buy_log,要进行如下的查询:

SELECT COUNT(*) FROM buy_log;

InnoDB存储引擎并不会选择通过查询聚集索引来进行统计。由于bug_log表上还有辅助索引,而辅助索引远小于聚集索引,选择辅助索引可以减少IO操作,故优化器的possible_keys列为NULL,但是实际执行时优化器却选择了userid索引,而列Extra列的Using index就是代表了优化器进行了覆盖索引操作。
此外,在通常情况下,诸如(a,b)的联合索引,一般是不可以选择列b中所谓的查询条件。但是如果是统计操作,并且是覆盖索引,则优化器会进行选择,如下述语句:

SELECT COUNT(*) FROM buy_log WHERE buy_date >= '2011-01-01' AND buy_date < '2011-01-02'

表buy_log中有(useridm buy_date)的联合索引,这里只根据列b进行条件查询,一般情况下是不能进行该联合索引的,但是这句SQL查询是统计操作,并且可以利用到覆盖索引的信息,因此优化器会选择该联合索引,在列Extra同样可以发现Using index提示,表示为覆盖索引。

优化器选择不使用索引的情况

在某些情况下,当执行EXPLAIN命令进行SQL语句的分析时,会发现优化器并没有选择索引去查找数据,而是通过扫描聚集索引,也就是直接进行全表的扫描来得到数据。这种情况多发生于范围查找、JOIN链接等情况下。例如:

SELECT * FROM orderdetails WHERE orderid > 10000 and orderid < 102000;

上述这句SQL语句查找订单号大于10000的订单详情,通过命令SHOW INDEX FROM orderdetails,可以看到表orderdetails有(OrderID,ProductID)的联合主键,此外还有对于列OrderID的单个索引。上述语句显然可以通过扫描OrderID上的索引进行数据的查找。然而通过EXPLAIN命令,用户发现优化器并没有按照OrderID上的索引来查找数据。
在possible_keys一列可以看到查询可以使用PRIMARY、OrderID、OrdersOrder_Details三个索引,但是最后的索引使用中,优化器选择了PRIMARY聚集索引,也就是表扫描(table scan),而非OrderID辅助索引扫描(index scan)。
这是为什么呢?原因在于用户要选取的数据是整行信息。而OrderID索引不能覆盖到我们要查询的信息,因此在对OrderID索引查询到指定数据后,还需要一次书签访问来查找整行数据的信息。虽然OrderID索引中数据是顺序存放的,但是再一次进行书签查找的数据则是无序的,因此变为了磁盘上的离散操作。如果要求访问的数据量很小,则优化器还是会选择辅助索引,但是当访问的数据占整个表中数据的大部分时(一般20%左右),优化器会选择通过聚集索引来查找数据。因为之前已经提到过,顺序读要远远快于离散读。
因此对于不能进行索引覆盖的情况,优化器选择辅助索引的情况是,通过辅助索引查找的数据是少量的。这是由当前传统机械硬盘的特性所决定的,即利用顺序读来替换随机读的查找。

Multi-Range Read优化

MySQL5.6版本开始支持Multi-Range Read(MRR)优化。Multi-Range Read优化的目的就是为了减少磁盘的随机访问,并且将随机访问转换为顺序的数据访问,这对于IO-bound类型的SQL可带来性能极大的提升。Multi-Range Read可适用于range、ref、eq_ref类型的查询。
MRR优化有以下几个好处:

  • MRR使数据访问变得较为顺序。在查询辅助索引时,首先根据得到的查询结果按照主键进行排序,并按照主键排序的顺序进行书签查找。
  • 减少缓冲池中页被替换的次数。
  • 批量处理对键值的查询操作。

对于InnoDB和MyISAM存储引擎的范围查询和JOIN查询操作,MRR的工作方式如下:

  • 将查询得到的辅助索引键值存放于一个缓存中,这时缓存中的数据是根据辅助索引键值排序的。
  • 将缓存中的键值根据RowID进行排序。
  • 根据RowID的排序顺序来访问实际的数据文件。

此外,若InnoDB存储引擎或者MyISAM存储引擎的缓冲池不是足够大,即不能存放下一张表中的所有数据,此时频繁的离散读操作还会导致缓存中的页被替换出缓冲池,然后又不断地被读入缓冲池。若是按照主键顺序进行访问,则可以将此重复行为降为最低。

Index Condition Pushdown(ICP)优化

和Multi-Range Read一样,Index Condition Pushdown同样是MySQL5.6开始支持的一种根据索引进行查询的优化方式。之前的MySQL数据库版本不支持Index Condition Pushdown,当进行索引查询时,首先根据索引来查找记录,然后再根据WHERE条件来过滤记录。在支持Index Condition Pushdown后,MySQL数据库会在取出索引的同时,判断是否可以进行WHERE条件过滤,也就是将WHERE部分过滤的操作放在了存储引擎层。在某些情况下,可以大大减少上层SQL层对记录的索取,从而提高数据库的整体性能。
Index Condition Pushdown优化支持range、ref、eq_ref、ref_or_null类型的查询,当前支持MyISAM和InnoDB存储引擎。当优化器选择Index Condition Pushdown优化时,可在执行计划的列Extra看到Using index condition提示。

全文检索

概述

通过前面章节的介绍,已经知道B+树索引的特点,可以通过索引字段的前缀进行查找。例如,对于下面的查询B+树索引是支持的:

SELECT * FROM blog WHERE content like 'xxx%'

上述SQL语句可以查询博客内容以xxx开头的文章,并且只要content条件了B+树索引,就能利用索引进行快速查询。然而实际这种查询不符合用户的要求,因为在更多的情况下,用户需要查询的是博客内容包含单次xxx的文章,即:

SELECT * FROM blog WHERE content like '%xxx%'

根据B+树索引的特性,上述SQL语句即便添加了B+树索引也是需要进行索引扫描得到结果。
全文检索(Full-Text Search)是将存储于数据库中的整本书或整篇文章中的任意内容信息查找出来的技术。它可以根据需要获得全文中有关章、节、段、句、词等信息,也可以进行各种统计和分析。

倒排索引

全文检索通常使用倒排索引(inverted index)来实现。倒排索引同B+树索引一样,也是一种索引结构。它在辅助表(auxiliary table)中存储了单词与单词自身在一个或多个文档中所在位置之间的映射。这通常利用关联数组实现,其拥有两种表现形式:

  • inverted file index,其表现形式为
  • full inverted index,其表现形式为

DocumentId表示进行全文检索文档Id,Text表示存储的内容,用户需要对存储这些文档内容进行全文检索。对于inverted file index的关联数组,可以根据Documents得到包含查询关键字的文档。对于inverted file index,其仅存取文档Id,而full inverted index存储的是对pair,即(Documentid, Position)。
full inverted index还存储了单词所在的位置信息。相比之下,full inverted index占用更多的空间,但是能更好地定位数据,并扩充一些其他的搜索特性。

InnoDB全文检索

InnoDB存储引擎从1.2.x版本开始支持全文检索的技术,其采用full inverted index的方式。在InnoDB存储引擎中,将(DocumentID,Position)视为一个“ilist”。因此在全文检索的表中,有两个列,一个是Word字段,另一个是ilist字段,并且在word字段上有设有索引。此外,由于InnoDB存储引擎在ilist字段中存放了position信息,故可以进行Proximity Search,而MyISAM存储引擎不支持该特性。
正如之前所说的那样,倒排索引需要将word存放到一张表中,这个表称为Auxiliary Table(辅助表)。在InnoDB存储引擎中,为了提高全文检索的并行性能,共有6张Auxiliary Table,目前每张表根据word的Latin编码进行分区。
Auxiliary Table是持久的表,存放在磁盘上。然而在InnoDB存储引擎的全文索引中,还有另外一个重要的概念FTS Index Cache(全文检索索引缓存),其用来提高全文检索的性能。
FTS Index Cache是一个红黑树结构,其根据(word,list)进行排序。这就意味着插入的数据已经更新了对应的表,但是对全文索引的更新可能在分词操作后还在FTS Index Cache中,Auxiliary Table可能还没有更新。InnoDB存储引擎会批量对Auxiliary Table进行更新,而不是每次插入后更新一次Auxiliary Table。当对全文检索进行查询时,Auxiliary Table首先会将在FTS Index Cache中对应的word字段合并到Auxiliary Table中,然后再进行查询。这种Merge操作非常类似之前介绍的Insert Buffer的功能,不同的是Insert Buffer是一个持久的对象,并且其是B+树的结构。然而FTS Index Cache的作用又和Insert Buffer是类似的,它提好了InnoDB存储引擎的性能,并且由于其根据红黑树排序后进行批量插入,其产生的Auxiliary Table相对较小。
InnoDB存储引擎允许用户查看指定倒排索引的Auxiliary Table中分词的信息,可以通过设置参数innodb_ft_aux_table来观察倒排索引的Auxiliary Table。
对于其他数据库,如Oracle 11g,用户可以选择手工在事务提交时,或者固定间隔时间时将倒排索引的更新刷新到磁盘。对于InnoDB存储引擎而言,其总是在事务提交时将分词写入到FTS Index Cache,然后再通过批量更新写入到磁盘。虽然InnoDB存储引擎通过一种延时的、批量写入方式来提高数据库的性能,但是上述操作仅在事务提交时发生。
当数据库关闭时,在FTS Index Cache中的数据库会同步到磁盘上的Auxiliary Table中。然而,如果当数据库发生宕机时,一些FTS Index Cache中的数据库可能未被同步到磁盘上。那么下次重启数据库时,当用户对表进行全文检索(查询或插入操作)时,InnoDB存储引擎会自动读取未完成的文档,然后进行分词操作,再将分词的结果放入到FTS Index Cache中。
参数innodb_ft_cache_size用来控制FTS Index Cache的大小,默认值为32M。当该缓存满时,会将其中的(word,ilist)分词信息同步到磁盘的Auxiliary Table中。增大该参数可以提高全文检索的性能,但是宕机时,未同步到磁盘中的索引信息可能需要更长时间进行恢复。
FTS Document ID是另外一个重要的概念。在Innodb存储引擎中,为了支持全文检索,必须由一个列与word进行映射,在innodb中这个列被命名为FTS_DOC_ID,其类型必须是BIGINT UNSIGNED NOT NULL,并且InnoDB存储引擎自动会在该列上加入一个名为FTS_DOC_ID_INDEX的Unique Index。上述这些操作都由InnoDB存储引擎自己完成,用户也可以在建表时自动添加FTS_DOC_ID,以及相应的Unique Index。
文档中分词的插入操作是在事务提交时完成,然而对于删除操作,其在事务提交时,不删除磁盘Auxiliary Table中的记录,而只是删除FTS Cache Index中的记录。对于Auxiliary Table中被删除的记录,InnoDB存储引擎会记录其FTS Document ID,并将其保存在DELETED auxiliary table中。在设置参数innodb_ft_aux_table后,用户同样可以访问information_schema架构下的表INNODB_FT_DELETED来观察删除的FTS Document ID。
当前InnoDB存储引擎的全文检索还存在以下的限制:

  • 每张表只能有一个全文检索的索引。
  • 由多列组合而成的全文检索的索引列必须使用相同的字符集与排序规则。
  • 不支持没有单词界定符(delimiter)的语言,如中文、日语、汉语等。

全文检索

MySQL数据库支持全文检索(Full-Text Search)的查询,其语法为:

MATCH (col1,col2,...) AGAINST (expr [search_modifier])
search_modifier:
  {
    IN NATURAL LANGUAGE MODE
    | IN NATURAL LANGUAGE MODE WITH QUERY EXPANSION 
    | IN BOOLEAN MODE | WITH QUERY EXPANSION
  }

MySQL数据库通过MATCH() … AGAINST()语法支持全文检索的查询,MATCH指定了需要被查询的列,AGAINST指定了使用何种方法去进行查询。
1、Natural Language
全文检索通过MATCH函数进行查询,默认采用Natural Language模式,其表示查询带有指定word的文档。

SELECT * FROM fts_a WHERE MATCH(body) AGAINST ('Porridge');

在WHERE条件中使用MATCH函数,查询返回的结果是根据相关性(Relevance)进行降序排序,即相关性最高的结果放在第一位。相关性的值是一个非负的浮点数字,0表示没有任何的相关性。根据MySQL官方的文档可知,其相关性的计算依据以下四个条件:

  • word是否在文档中出现。
  • word在文档中出现的次数。
  • word在索引列中的数量。
  • 多少个文档包含该word。