MySQL中InnoDB全文检索
InnoDB存储引擎从1.2.x开始支持全文索引技术,其采用full inverted index的方式。在InnoDB存储引擎中,将(DocumentID,Postition)视为一个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,ilist)进行排序,这意味着插入的数据已更新了对应的表,但是对全文索引的更新可能在粉刺操作后还在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 下面的SQL 语句设置查看test架构下表fts_a的Auxiliary Table:
SET GLOBAL innodb_ft_aux_table='test/fts_a';
可以在information_schema架构下的表INNODB_FT_INDEX_TABLE得到表fts_a中的分词信息。
对于InnoDB存储引擎而言,其总是在事务提交时将分词写入到FTS Index Cache,然后通过批量写入到磁盘。虽然InnoDB存储引擎通过一种延时的、批量的写入方式来提高数据库的性能,但是上述操作仅在事务提交时发生。
当数据库关闭时,在FTS Index Cache中的数据库会同步到磁盘上的Auxiliary Table中。如果当数据库发生宕机时,一些FTS Index Cache中的数据可能未同步到磁盘上,那么下次重启数据库时,当用户对表进行全文检索(查询、插入)时,InnoDB存储引擎会自动读取未完成的文档,然后进行分词操作,再将分词结果放到FTS Index Cache
为了支持全文检索,必须有一个列与word进行映射。在InnoDB中这个列被命名成FTS_DOC_ID,其类型为BIGINT UNSIGNED NOT NULL,并且InnoDB存储引擎自动会在该列加上一个名为FTS_DOC_ID_INDEX的Unique Index.这些操作由存储引擎自己完成,用户也可以在建表时自动添加FTS_DOC_ID,以及对应的Unique Index。由于列名FTS_DOC_ID聚友特殊意义,因此在创建时必须注意相应的类型,否则会报错
可以看到,由于用户手动定义FTS_DOC_ID为INT,而非BIGINT因此在创建时候会抛出异常,应该将此处修改成对应的BIGINT即可
文档中的分词的插入操作是在事务提交时完成,但是对于删除操作,其在事务提交时,不删除磁盘Auxiliary Table的记录,而只是删除FTS Cache Index记录,对于Auxiliary Table中被删除的记录,存储引擎会记录其FTS DOCUMENT ID ,并将其保存在DELETE auxiliary table中,在设置参数innodb_ft_aux_table后,用户可以访问information_schema架构下的表INNODB_FT_DELETED来观察删除的FTS Document ID
由于文档的DML操作实际并不删除索引中的数据,相反还会在对应的DELETED表中插入记录,因此随着应用程序的允许,索引会变得越来越大,即使索引中的有些数据已经被删除,查询也不会选择这类记录,为此,InnoDB提供了一种方式,允许用户手工将已删除的记录从索引中彻底删除,这就是OPTIMIZE TABLE。因为OPTIMIZE TABLE还会进行一些其他的操作。如Cardinality重新统计,若用户希望对倒排索引进行操作,可以通过innodb_optimize_fulltext_only设置
SET GLOBAL innodb_optimize_fulltext_only=1; OPTIMIZE TABLE fts_a;
若被删除的文档很多,那么OPTIMIZE TABLE操作可能占用非常多的时间,会影响到程序并发性,并极大的降低用户的响应时间,用户可以通过参数innodb_ft_num_word_optimize来限制每次实际删除的分词数量,默认为2000
CREATE TABLE fts_a(
FTS_DOC_ID BIGINT UNSIGNED AUTO_INCREMENT NOT NULL,
body TEXT,
PRIMARY KEY(FTS_DOC_ID)
); INSERT INTO fts_a SELECT NULL,'pease porridge in the post';
INSERT INTO fts_a SELECT NULL,'pease porridge hot,pease porridge cold';
INSERT INTO fts_a SELECT NULL,'Nine days old';
INSERT INTO fts_a SELECT NULL,'Some like it hot,some like it cold';
INSERT INTO fts_a SELECT NULL,'Some like it the pot';
INSERT INTO fts_a SELECT NULL,'Nine days old';
INSERT INTO fts_a SELECT NULL,'I like code days'; CREATE FULLTEXT INDEX idx_fts ON fts_a(body);
查看数据
mysql> select * from fts_a;
+------------+----------------------------------------+
| FTS_DOC_ID | body |
+------------+----------------------------------------+
| 1 | pease porridge in the post |
| 2 | pease porridge hot,pease porridge cold |
| 3 | Nine days old |
| 4 | Some like it hot,some like it cold |
| 5 | Some like it the pot |
| 6 | Nine days old |
| 7 | I like code days |
+------------+----------------------------------------+
7 rows in set (0.00 sec)
mysql> set global innodb_ft_aux_table='iot2/fts_a';
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT * FROM information_schema.`INNODB_FT_INDEX_TABLE`;
+----------+--------------+-------------+-----------+--------+----------+
| WORD | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION |
+----------+--------------+-------------+-----------+--------+----------+
| code | 7 | 7 | 1 | 7 | 7 |
| cold | 2 | 4 | 2 | 2 | 34 |
| cold | 2 | 4 | 2 | 4 | 30 |
| days | 3 | 7 | 3 | 3 | 5 |
| days | 3 | 7 | 3 | 6 | 5 |
| days | 3 | 7 | 3 | 7 | 12 |
| hot | 2 | 4 | 2 | 2 | 15 |
| hot | 2 | 4 | 2 | 4 | 13 |
| like | 4 | 7 | 3 | 4 | 5 |
| like | 4 | 7 | 3 | 4 | 17 |
| like | 4 | 7 | 3 | 5 | 5 |
| like | 4 | 7 | 3 | 7 | 2 |
| nine | 3 | 6 | 2 | 3 | 0 |
| nine | 3 | 6 | 2 | 6 | 0 |
| old | 3 | 6 | 2 | 3 | 10 |
| old | 3 | 6 | 2 | 6 | 10 |
| pease | 1 | 2 | 2 | 1 | 0 |
| pease | 1 | 2 | 2 | 2 | 0 |
| pease | 1 | 2 | 2 | 2 | 19 |
| porridge | 1 | 2 | 2 | 1 | 6 |
| porridge | 1 | 2 | 2 | 2 | 6 |
| porridge | 1 | 2 | 2 | 2 | 19 |
| post | 1 | 1 | 1 | 1 | 22 |
| pot | 5 | 5 | 1 | 5 | 17 |
| some | 4 | 5 | 2 | 4 | 0 |
| some | 4 | 5 | 2 | 4 | 17 |
| some | 4 | 5 | 2 | 5 | 0 |
+----------+--------------+-------------+-----------+--------+----------+
27 rows in set (0.00 sec)
可以看到每个word对应一个DOC_ID和POSITION。此外,还记录了FIRST_DOC_ID、LAST_DOC_ID、DOC_COUNT分别代表该word第一次出现文档的ID,最后一次出现的文档ID,以及该word在多少个文档中存在。
若此时执行下面的SQL语句,会删除FTS_DOC_ID为7的文档
DELETE FROM fts_a WHERE FTS_DOC_ID=7;
InnoDB存储引擎并不会直接删除索引中对应的记录,而是将删除的文档ID插入到DELETED表
SELECT * FROM information_schema.`INNODB_FT_DELETED`;
如果用户想要彻底删除倒排索引中该文档的分词信息,可以
mysql> SET GLOBAL innodb_optimize_fulltext_only=1;
Query OK, 0 rows affected (0.00 sec) mysql> OPTIMIZE TABLE fts_a;
+------------+----------+----------+----------+
| Table | Op | Msg_type | Msg_text |
+------------+----------+----------+----------+
| iot2.fts_a | optimize | status | OK |
+------------+----------+----------+----------+
1 row in set (0.08 sec) mysql> SELECT * FROM information_schema.`INNODB_FT_DELETED`;
+--------+
| DOC_ID |
+--------+
| 7 |
+--------+
1 row in set (0.00 sec) mysql> SELECT * FROM information_schema.`INNODB_FT_BEING_DELETED`;
+--------+
| DOC_ID |
+--------+
| 7 |
+--------+
1 row in set (0.00 sec)
运行OPTIMIZE TABLE 可以将记录彻底删除,并且彻底删除的文档ID会记录到INNODB_FT_BEGIN_DELETED中。此外,由于7这个文档一倍删除,因此不允许在插入这个文档ID,否则会抛出异常
mysql> INSERT INTO fts_a SELECT 7,'I like this days';
ERROR 182 (HY000): Invalid InnoDB FTS Doc ID
stopword列表(stopword list)是本节最后阐述的一个概念,其表示该列表中的word不需要对其进行索引分词操作。例如,对于the这个单词,由于其不具有具体的意义,因此将其视为stopword,InnoDB存储引擎有一张默认的stopword列表,在information_schema架构下,表名为INNODB_FT_DEFAULT_STOPWORD,默认为36个stopword可以通过参数innodb_ft_server_stopword_table来定义stopword列表,如
mysql> CREATE TABLE innodb_ft_bug (
-> value VARCHAR(18) NOT NULL DEFAULT ''
-> ) ENGINE=INNODB DEFAULT CHARSET=utf8; #此处必须为utf8不然会碰到bug
Query OK, 0 rows affected (0.07 sec) mysql> SET GLOBAL innodb_ft_server_stopword_table='iot2/innodb_ft_bug';
Query OK, 0 rows affected (0.00 sec)
遇到bug的情形
mysql> CREATE TABLE user_stopword(VALUE VARCHAR(30))ENGINE=INNODB;
Query OK, 0 rows affected (0.03 sec)
mysql> SET GLOBAL innodb_ft_server_stopword_table='iot2/user_stopword';
ERROR 1231 (42000): Variable 'innodb_ft_server_stopword_table' can't be set to the value of 'iot2/user_stopword' 观察错误日志提示
InnoDB: invalid column name for stopword table iot2/user_stopword. Its first column must be named as 'value'.
使用全文检索还有以下限制
每张表只能有一个全文检索的索引
由多列组合而成的全文检索的索引必须使用相同的字符集与排序规则
不支持没有单词界定符delimiter的语言,如中文 日文汉语等
MySQL中InnoDB全文检索的更多相关文章
- mysql 中 innoDB 与 MySAM
mysql 中 innoDB 与 MyISAM 的特点 --ENGINE = innodb 1.提供事务处理,支持行锁: 2.不加锁读取,增加并发读的用户数量和空间: 3. insert/update ...
- MySql中innodb存储引擎事务日志详解
分析下MySql中innodb存储引擎是如何通过日志来实现事务的? Mysql会最大程度的使用缓存机制来提高数据库的访问效率,但是万一数据库发生断电,因为缓存的数据没有写入磁盘,导致缓存在内存中的数据 ...
- MySQL中InnoDB锁不住表的原因
MySQL中InnoDB锁不住表是因为如下两个参数的设置: mysql> show variables like '%timeout%'; +-------------------------- ...
- 面试官:为什么Mysql中Innodb的索引结构采取B+树?
前言 如果面试官问的是,为什么Mysql中Innodb的索引结构采取B+树?这个问题时,给自己留一条后路,不要把B树喷的一文不值.因为网上有些答案是说,B树不适合做文件存储系统的索引结构.如果按照那种 ...
- MySQL中innodb引擎分析(初始化)
MySQL的存储引擎是以插件形式工作的,这应该是MySQL的一大特色了吧! 依据<深入理解MySQL>的内容,5.1版本号时存储引擎的插件化都还不是彻底,确切的说是刚加入的特性.为MySQ ...
- mysql中InnoDB存储引擎的行锁和表锁
Mysql的InnoDB存储引擎支持事务,默认是行锁.因为这个特性,所以数据库支持高并发,但是如果InnoDB更新数据的时候不是行锁,而是表锁的话,那么其并发性会大打折扣,而且也可能导致你的程序出错. ...
- 《MySQL实战45讲》学习笔记4——MySQL中InnoDB的索引
索引是在存储引擎层实现的,且在 MySQL 不同存储引擎中的实现也不同,本篇文章介绍的是 MySQL 的 InnoDB 的索引. 下文将以这张表为例开展. # 创建一个主键为 id 的表,表中有字段 ...
- MySQL中InnoDB存储引擎的实现和运行原理
InnoDB 存储引擎作为我们最常用到的存储引擎之一,充分熟悉它的的实现和运行原理,有助于我们更好地创建和维护数据库表. InnoDB 体系架构 InnoDB 主要包括了: 内存池.后台线程以及存储文 ...
- (转)mysql中InnoDB表为什么要建议用自增列做主键
InnoDB引擎表的特点 1.InnoDB引擎表是基于B+树的索引组织表(IOT) 关于B+树 (图片来源于网上) B+ 树的特点: (1)所有关键字都出现在叶子结点的链表中(稠密索引),且链表中的关 ...
随机推荐
- ImageLoader使用
最近琢磨的网上的这些开源框架,imageLoader确实很好用,基本用法不记录了,参考网上的一些代码写了个ImageLoaderManager /** * Created by Lee on 2016 ...
- Web服务器性能/压力测试工具http_load、webbench、ab、Siege、loadrunner
回头看看 Web服务器性能/压力测试工具http_load.webbench.ab.Siege.loadrunner
- NIO之管道 (Pipe)
Java NIO 管道是2个线程之间的单向数据连接.Pipe有一个source通道和一个sink通道.数据会被写到sink通道,从source通道读取. 代码使用示例: public static v ...
- 10710 - Chinese Shuffle(数论+完美洗牌)
UVA 10710 - Chinese Shuffle 题目链接 题意:给定n张牌,完美洗牌n - 1次,问是否会变回原来的序列 思路:完美洗牌: 如果有a1a2a3...anb1b2b3...bn的 ...
- RapidIOIP核的验证方法研究_王玉欢
RapidIOIP核的验证方法研究_王玉欢 https://wenku.baidu.com/view/0fd3c925d4d8d15abf234e73.html
- First MFC
// stdafx.h : include file for standard system include files, // or project specific include files t ...
- 转:NHibernate 存储过程
http://stackoverflow.com/questions/928847/how-to-get-the-return-value-from-a-sql-server-stored-proce ...
- YUYV格式到RGB格式的转换
为什么YUYV格式要转到RGB格式,视频的显示调用的多数API都是基于RGB格式,所以需要进行格式的转换. YUYV格式如下: Y0U0Y1V0 Y2U1Y3V1.......... 说明:一个Y代表 ...
- 561. Array Partition I【easy】
561. Array Partition I[easy] Given an array of 2n integers, your task is to group these integers int ...
- Mat::operator =
Provides matrix assignment operators. C++: Mat& Mat::operator=(const Mat& m) C++: Mat& M ...