全文索引的原理及定义

全文索引时将存储在数据库中的整本书或整篇文章中的任意内容信息查找出来的技术。它可以根据需要获取全文中有关章,节,段,句,词等信息,也可以进行各种统计和分析。

之前的MySQL数据库中,INNODB存储引擎并不支持全文索引技术,大多数的用户转向MyISAM存储引擎,不过这可能进行表的拆分,并需要将进行全文索引的数据存储为MyIsam表。这样的确能够解决逻辑业务的需求,但是却丧失了INNODB存储引擎的事务性,而这在生产环境应用中同样是非常关键的。

从INNODB1.2.x版本开始,INNODB存储引擎开始支持全文索引,其支持myisam的全部功能,并且还支持其他的一些特性。

倒排索引

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

  • inverted file index, 其表现形式为{单词, 单词所在文档的ID}
  • full inverted index,其表现形式为{单词,(单词所在文档的ID, 再具体文档中的位置)}

【具体来说就是inverted file index只能根据单词找到对应的文档,而full inverted index不仅能找到对应的文档,还能找到单词在文档中的具体位置】

INNODB全文索引

INNODB存储从1.2版本开始支持全文索引的技术,其采用full inverted index的技术。在INNODB存储引擎中将(documentid, position)视为一个“ilist”。因此在全文索引的表中,有两个列,一个是word字段,另一个是ilist字段,并且在word字段上设有索引。此外,INNODB存储引擎在ilist字段上存放了position信息,故可以进行proximity  search(邻近查找),而myisam不支持这个。

倒排索引需要将word字段存放在一张表中,这个表称为辅助表。在INNODB存储引擎中,为了提高全文检索的并行性能,共有6张辅助表,目前每张表根据word的latin编码进行分区。辅助表时持久的表,存放于磁盘上。然而在innodb存储引擎的全文索引中,还有另外一个重要的概念FTS index cache(全文索引缓存),其用来提高全文检索的性能。FTS index cache是一个红黑树结构,其根据(word, ilist)进行排序。这意味着插入的数据已经更新了对应的表,但是对全文索引的更新可能在分词操作后还在FTS index cache中,辅助表可能还没有更新。innodb存储引擎会批量对辅助表进行更新,而不是每次插入后更新一次辅助表。当对全文索引进行查询时,辅助表会首先将FTS index cache中对应的word字段合并到辅助表中,然后再进行查询。

innodb存储引擎允许用户查看指定倒排索引的辅助表中的分词信息,可以通过设置参数innodb_ft_aux_table来观察倒排索引的辅助表。

mysql> set global innodb_ft_aux_table="mytest/tb1";           #对应的表上必须有全文索引,不然会报错。
Query OK, 0 rows affected (0.00 sec)

mysql> use information_schema;                                #在information_schema库中,查看对应表的分词信息Reading table information for completion of table and column namesYou can turn off this feature to get a quicker startup with -A

Database changedmysql> desc innodb_ft_index_table;+--------------+---------------------+------+-----+---------+-------+| Field        | Type                | Null | Key | Default | Extra |+--------------+---------------------+------+-----+---------+-------+| WORD         | varchar(337)        | NO   |     |         |       || FIRST_DOC_ID | bigint(21) unsigned | NO   |     | 0       |       || LAST_DOC_ID  | bigint(21) unsigned | NO   |     | 0       |       || DOC_COUNT    | bigint(21) unsigned | NO   |     | 0       |       || DOC_ID       | bigint(21) unsigned | NO   |     | 0       |       || POSITION     | bigint(21) unsigned | NO   |     | 0       |       |+--------------+---------------------+------+-----+---------+-------+6 rows in set (0.00 sec)

mysql> 

#对于innodb存储引擎,总是在事务提交时将分词写入FTS Index Cache,然后再通过批量更新写入到磁盘。虽然INNODB存储引擎通过一种延时,批量的写入方式来提高数据库的性能,#但是上述操作仅在事务提交时发生。

当数据库关闭时,在FTS Index Cache中的数据库会同步到磁盘上的辅助表。然而,如果当数据库发生宕机时,一些FTS Index Cache中的数据可能未被同步到磁盘上。那么下次重启数据库时,当用户对表进行全文索引时,INNODB存储引擎会自动读取未完成的文档,然后进行分词操作,再将分词的结果放入到FTS Index Cache中。

参数innodb_ft_cache_size用来控制FTS Index Cache的大小。当该缓存满时,会将其中的(word,ilist)分词信息同步到磁盘的辅助表中。增大该参数可以提高数据库的全文索引性能,但是在宕机时,未同步到磁盘中的索引信息可能需要更长时间恢复。

在innodb存储引擎中,为了支持全文检索,必须有一个列与word进行映射,在innodb中这个列被命名为FTS_DOC_ID,其类型必须为BIGINT UNSIGNED NOT NULL,并且innodb存储引擎会在该列上加上一个名为FTS_DOC_ID_INDEX的唯一索引。上述操作由innodb存储引擎自己完成,用户也可以在创建表时手动添加,主要对应的约束条件。

文档中分词的插入操作是在事务提交时完成,然而对于删除操作,其再事务提交时,不删除磁盘辅助表中的记录,而只是删除FTS cache index中的记录。对于辅助表中被删除的记录,innodb存储引擎会记录其FTS Document id,并将其保存在deleted辅助表中。在设置innodb_ft_aux_table后,用户同意可以方位information_schema架构下的表innodb_ft_deleted来观察删除的FTS Document ID。

由于文档的DML操作实际并不删除索引中的数据,相反还会在对应deleted表中插入记录,因此随着应用程序的允许,索引会变得非常大,即使索引中的有些数据已经被删除,查询也不会选择这类记录。innodb提供了一种方式,允许用户手工地将已经删除的记录从索引中彻底删除,该命令就是OPTIMIZE TABLE.因为这个命令还会进行一些其他的操作,如cardinality的重新统计等,若用户希望仅对倒排索引进行操作,可以通过如下参数设置。

mysql> set global innodb_optimize_fulltext_only = ON;
Query OK, 0 rows affected (0.00 sec)

mysql> 

若删除的文档非常多,那么OPTIMIZE TABLE操作可能需要占用非常多的时间,这样会影响应用程序的并发性,并极大地降低用户的响应时间。用户可以通过innodb_ft_num_word_optimize来限制每次实际删除分词的数量。该参数默认数值为2000.

mysql> show variables like "innodb_ft_num_word_optimize";
+-----------------------------+-------+
| Variable_name               | Value |
+-----------------------------+-------+
| innodb_ft_num_word_optimize | 2000  |
+-----------------------------+-------+
1 row in set (0.00 sec)

mysql>

上面提到过创建表的全文索引时,还有一个列叫做FTS_DOC_ID;这个列我们可以自己在创建表时创建,也可以让innodb存储引擎自动创建,innodb存储引擎自动创建的是隐藏的列。但是在官方文档中建议自己创建。

Defining an FTS_DOC_ID column at CREATE TABLE time is less expensive than creating a full-text index on a table that is already loaded with data. If an FTS_DOC_ID column is defined on a table prior to loading data, the table and its indexes do not have to be rebuilt to add the new column. If you are not concerned with CREATE FULLTEXT INDEX performance, leave out the FTS_DOC_ID column to have InnoDB create it for you. InnoDB creates a hidden FTS_DOC_ID column along with a unique index (FTS_DOC_ID_INDEX) on the FTS_DOC_ID column. If you want to create your own FTS_DOC_ID column, the column must be defined as BIGINT UNSIGNED NOT NULL and named FTS_DOC_ID (all upper case), as in the following 

官方文档:https://dev.mysql.com/doc/refman/5.7/en/innodb-fulltext-index.html

下面演示一个实例:

create table tb3(FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL,body text,FULLTEXT INDEX ft_index (body),UNIQUE INDEX FTS_DOC_ID_INDEX (FTS_DOC_ID));

#上面提到过,除了创建FTS_DOC_ID,还要在这个字段上创建一个名为FTS_DOC_ID_INDEX的唯一索引;

在表中插入数据:mysql> insert into tb3 select NULL, "pLease porridge in the pot";Query OK, 1 row affected (0.01 sec)Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into tb3 select NULL,"please say sorry";Query OK, 1 row affected (0.01 sec)Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into tb3 select NULL,"nine years old";Query OK, 1 row affected (0.00 sec)Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into tb3 select NULL,"some like it hot, some like it cold";Query OK, 1 row affected (0.00 sec)Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into tb3 select NULL,"i like coding";Query OK, 1 row affected (0.00 sec)Records: 1  Duplicates: 0  Warnings: 0

mysql> insert into tb3 select NULL,"fuck the company";Query OK, 1 row affected (0.01 sec)Records: 1  Duplicates: 0  Warnings: 0

然后设置innodb_ft_aux_table的值,查看分词信息:

mysql> select * from tb3;
+------------+-------------------------------------+
| FTS_DOC_ID | body                                |
+------------+-------------------------------------+
|          1 | pLease porridge in the pot          |
|          2 | please say sorry                    |
|          3 | nine years old                      |
|          4 | some like it hot, some like it cold |
|          5 | i like coding                       |
|          6 | fuck the company                    |
+------------+-------------------------------------+
6 rows in set (0.00 sec)

mysql> set global innodb_ft_aux_table="mytest/tb3";         #设置查看分词信息
Query OK, 0 rows affected (0.00 sec)

mysql> use information_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> select * from innodb_ft_index_table;                #MySQL5.7中加入了一个cache表,这时候table表时空的
Empty set (0.01 sec)

mysql> select * from innodb_ft_index_cache;
+----------+--------------+-------------+-----------+--------+----------+
| WORD     | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION |
+----------+--------------+-------------+-----------+--------+----------+
| coding   |            5 |           5 |         1 |      5 |        7 |
| cold     |            4 |           4 |         1 |      4 |       31 |
| company  |            6 |           6 |         1 |      6 |        9 |
| fuck     |            6 |           6 |         1 |      6 |        0 |
| hot      |            4 |           4 |         1 |      4 |       13 |
| like     |            4 |           5 |         2 |      4 |        5 |
| like     |            4 |           5 |         2 |      4 |       18 |
| like     |            4 |           5 |         2 |      5 |        2 |
| nine     |            3 |           3 |         1 |      3 |        0 |
| old      |            3 |           3 |         1 |      3 |       11 |
| please   |            1 |           2 |         2 |      1 |        0 |
| please   |            1 |           2 |         2 |      2 |        0 |
| porridge |            1 |           1 |         1 |      1 |        7 |
| pot      |            1 |           1 |         1 |      1 |       23 |
| say      |            2 |           2 |         1 |      2 |        7 |
| some     |            4 |           4 |         1 |      4 |        0 |
| some     |            4 |           4 |         1 |      4 |       18 |
| sorry    |            2 |           2 |         1 |      2 |       11 |
| years    |            3 |           3 |         1 |      3 |        5 |
+----------+--------------+-------------+-----------+--------+----------+
19 rows in set (0.00 sec)

有关innodb_ft_index_cache的作用:

The INNODB_FT_INDEX_CACHE table provides token information about newly inserted rows in a FULLTEXT index. To avoid expensive index reorganization during DML operations, the information about newly indexed words is stored separately, and combined with the main search index only when OPTIMIZE TABLE is run, when the server is shut down, or when the cache size exceeds a limit defined by the innodb_ft_cache_size or innodb_ft_total_cache_size system variable.

#INNODB_FT_INDEX_CACHE提供关于新插入全文索引行的信息,为了避免在DML操作期间,昂贵的索引重组,新索引字段的信息单独存储。只有当运行OPTIMIZE TABLE命令,或者服务器关机,或者缓存的大小超过innodb_ft_cache_size或者innodb_ft_total_cache_size的限制时,才会合并索引信息。

执行命令,合并全文索引的信息:

mysql> set global innodb_optimize_fulltext_only = ON;
Query OK, 0 rows affected (0.00 sec)

mysql> optimize table mytest.tb3;
+------------+----------+----------+----------+
| Table      | Op       | Msg_type | Msg_text |
+------------+----------+----------+----------+
| mytest.tb3 | optimize | status   | OK       |
+------------+----------+----------+----------+
1 row in set (0.02 sec)

mysql> select * from innodb_ft_index_cache;             #执行之后,缓存中没有数据,table表中有对应的分词信息。
Empty set (0.00 sec)

mysql> select * from innodb_ft_index_table;
+----------+--------------+-------------+-----------+--------+----------+
| WORD     | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION |
+----------+--------------+-------------+-----------+--------+----------+
| coding   |            5 |           5 |         1 |      5 |        7 |
| cold     |            4 |           4 |         1 |      4 |       31 |
| company  |            6 |           6 |         1 |      6 |        9 |
| fuck     |            6 |           6 |         1 |      6 |        0 |
| hot      |            4 |           4 |         1 |      4 |       13 |
| like     |            4 |           5 |         2 |      4 |        5 |
| like     |            4 |           5 |         2 |      4 |       18 |
| like     |            4 |           5 |         2 |      5 |        2 |
| nine     |            3 |           3 |         1 |      3 |        0 |
| old      |            3 |           3 |         1 |      3 |       11 |
| please   |            1 |           2 |         2 |      1 |        0 |
| please   |            1 |           2 |         2 |      2 |        0 |
| porridge |            1 |           1 |         1 |      1 |        7 |
| pot      |            1 |           1 |         1 |      1 |       23 |
| say      |            2 |           2 |         1 |      2 |        7 |
| some     |            4 |           4 |         1 |      4 |        0 |
| some     |            4 |           4 |         1 |      4 |       18 |
| sorry    |            2 |           2 |         1 |      2 |       11 |
| years    |            3 |           3 |         1 |      3 |        5 |
+----------+--------------+-------------+-----------+--------+----------+
19 rows in set (0.00 sec)

删除全文索引的一行记录:

mysql> delete from tb3 where fts_doc_id=3;
Query OK, 1 row affected (0.05 sec)

mysql> select * from information_schema.innodb_ft_deleted;
+--------+
| DOC_ID |
+--------+
|      3 |
+--------+
1 row in set (0.00 sec)

#The INNODB_FT_DELETED table stores rows that are deleted from the FULLTEXT index for an InnoDB table. To avoid expensive index reorganization during DML operations for an InnoDB FULLTEXT index, the information about newly deleted words is stored separately, filtered out of search results when you do a text search, and removed from the main search index only when you issue an OPTIMIZE TABLE statement for the InnoDB table. 

#删除的时候并不会真实的删除,只是把删除的row记录在INNODB_FT_DELETED表中,查询的时候会过滤掉,如果你执行了OPTIMIZE TABLE语句,那么才会真正的删除。
mysql> set global innodb_optimize_fulltext_only = ON;
Query OK, 0 rows affected (0.00 sec)

mysql> optimize table mytest.tb3;                         #执行完这条语句之后,才会真正的删除。
+------------+----------+----------+----------+
| Table      | Op       | Msg_type | Msg_text |
+------------+----------+----------+----------+
| mytest.tb3 | optimize | status   | OK       |
+------------+----------+----------+----------+
1 row in set (0.03 sec)

mysql> select * from information_schema.innodb_ft_being_deleted; +--------+| DOC_ID |+--------+|      3 |+--------+1 row in set (0.00 sec)

#The INNODB_FT_BEING_DELETED table provides a snapshot of the INNODB_FT_DELETED table; it is used only during an OPTIMIZE TABLE maintenance operation. When OPTIMIZE TABLE is run, the INNODB_FT_BEING_DELETED table is emptied, and DOC_ID values are removed from the INNODB_FT_DELETED table. Because the contents of INNODB_FT_BEING_DELETED typically have a short lifetime, this table has limited utility for monitoring or debugging.

#INNODB_FT_BEING_DELETED表是INNODB_FT_DELETED的快照,在手动运行OPTIMIZE TABLE期间使用,当OPTIMIZE TABLE运行时,INNODB_FT_BEING_DELETED表被清空,并且DOC_ID#的值从INNODB_FT_DELETED中删除,因为INNODB_FT_BEING_DELETED表中的内容生命周期很短,因此使用这个表的作用是有限的。

stopword:在分词操作时,有些词因为没有实际的意义,并不需要对其进行分次操作。例如,对于定冠词the。在innodb存储引擎中有一张默认的stopword表,如下!

mysql> show create table information_schema.INNODB_FT_DEFAULT_STOPWORD\G
*************************** 1. row ***************************
       Table: INNODB_FT_DEFAULT_STOPWORD
Create Table: CREATE TEMPORARY TABLE `INNODB_FT_DEFAULT_STOPWORD` (
  `value` varchar(18) NOT NULL DEFAULT ‘‘
) ENGINE=MEMORY DEFAULT CHARSET=utf8
1 row in set (0.00 sec)

mysql> 

除了使用innodb存储引擎默认的stopword表,用户还可以使用innodb_ft_server_stopword_table来定义stopword列表。

mysql> create table user_stopword( value varchar(30) );
Query OK, 0 rows affected (0.04 sec)

mysql> set global innodb_ft_server_stopword_table="mytest/user_stopword";
Query OK, 0 rows affected (0.00 sec)

innodb存储引擎的全文索引还存在以下限制:

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

博文中的内容摘自: MySQL技术内幕-innodb存储引擎。以及官方文档。

原文地址:https://www.cnblogs.com/wxzhe/p/9955534.html

时间: 2024-10-18 17:50:10

全文索引的原理及定义的相关文章

数据库原理-数据定义

数据定义 基本表的定义.删除与修改 定义 格式:create table <表名>(<列名><数据类型>[列级完整性约束条件],<列名><数据类型>[列级完整性约束条件]...,[表级完整性约束条件]); 实例1: CREATE TABLE student ( id INT (11) PRIMARY KEY, NAME VARCHAR (11) UNIQUE); 实例2: CREATE TABLE student ( id INT (11), N

利用反射的原理来自定义Tab 下划线indicator的长度

public class SetIndicator { public static void setIndicator(Context context, TabLayout tabs, int leftDip, int rightDip) { Class<?> tabLayout = tabs.getClass(); Field tabStrip = null; try { tabStrip = tabLayout.getDeclaredField("mTabStrip")

(4.15)全文索引的使用

全文索引的概念:搜索引擎如何工作? 总结就是一句话:直接看倒排索引图. 总结: 词组包含所有存在行的唯一标识. 信息检索已经发展的非常成熟了,应该所有人都不陌生.我有幸这几年接触过并且实际做过一些搜索引擎开发的工作,特此总结并分享给大家.实际上,一个成熟的搜索引擎是想当复杂的,比如百度的,就分nginx,vui,us,as,bs,da.....等等这些模块,当然这些简写的字母大家也不必了解,只要知道它确实复杂就可以. 今天我所讲的是一个简化版的搜索引擎,简化到只涉及到倒排建立和拉取.虽然简单,但

spring IOC的实现原理

姓名:罗秀群    班级:软件151 IOC的意思是控件反转也就是由容器控制程序之间的关系,把控件权交给了外部容器,之前的写法,由程序代码直接操控,而现在控制权由应用代码中转到了外部容器,控制权的转移是所谓反转.  IOC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象.这一点是通过DI(Dependency Injection,依赖注入)来实现的. 下面来模拟下IOC和DI的实现原理. 1.首先定义DAO接口和接口的实现类 package com.dao; public int

递归函数原理

递归函数实现原理 函数定义:就是函数体的实现.函数体就是一个代码块,它在函数被调用时执行. 函数声明:向编译器提供该函数的相关的信息,包括:参数的数量,每个参数的类型以及返回值的类型.用于函数被正确的调用. [注意]:当一个函数被调用时,编译器如果无法看到它的任何声明,那么它就假定函数返回一个整型值.对于那些返回值不是整型的函数,在调用之前对它们进行声明是非常重要的,这可以避免由于不可预测的类型转换而导致的错误.对于那些没有原型的函数,传递给函数的实参将进行缺省参数提升:char和short类型

SHA-256算法和区块链原理初探

组内技术分享的内容,目前网上相关资料很多,但读起来都不太合自己的习惯,于是自己整理并编写一篇简洁并便于(自己)理解和分享的文章. 因为之前对密码学没有专门研究,自己的体会或理解会特别标注为"个人理解",请注意甄别,如有必要可以自行查证. 阅读前需要树立一种观点:大部分场景都是基于概率的大小而言的,比如SHA256安全性.区块链不可更改性等. SHA-256算法 简介 区块链的基础算法之一,在其中用于区块hash计算方法. 是SHA-2下的一个算法标准,而SHA-2全称安全散列算法2,即

DHCP 协议原理以及如何配置 DHCP

DHCP 协议原理DHCP -定义-作用 -原理 DHCP 工作过程是 c/s ,即 客服端/服务器模式:-设备角色区分: DHCP 客户端 -终端设备 (PC.网络摄像头.IP电话.网络打印机等) # DHCP服务器 -软件: DHCP服务器软件 -硬件: 服务器 Linux : 直接开启 DHCP 服务: Windows -用户版:安装一个DHCP软件: -服务器版:开启DHCP服务就可以: 路由器 交换机 -DHCP报文 发送方式都是广播 1. dhcp discover // 为了请求一

JavaScript模板引擎原理与用法

这篇文章主要介绍了JavaScript模板引擎原理与用法,结合实例形式详细分析了javascript模版引擎相关概念.原理.定义及使用方法,写的十分的全面细致,具有一定的参考价值,对此有需要的朋友可以参考学习下 一.前言 什么是模板引擎,说的简单点,就是一个字符串中有几个变量待定.比如: var tpl = 'Hei, my name is <%name%>, and I\'m <%age%> years old.'; 通过模板引擎函数把数据塞进去, var data = { &q

加密传输原理

数字签名,就是通过在数据单元上附加数据,或对数据单元进行秘密变换,从而使接收者可以确认数据来源和完整性.简单说来,数字签名是防止他人对传输的文件进行破坏,以及确定发信人的身份的手段. 目前的数字签名是建立在公共密钥体制基础上,它是公用密钥加密技术的另一类应用.它的主要方式是:报文的发送方从报文文本中生成一个128位的散列值(又称报文摘要,数字指纹).发送方用自己的私人密钥对这个散列值进行加密来形成发送方的数字签名.然后,这个数字签名将作为报文的附件和报文一起发送给报文的接收方.报文的接收方首先从