普通DML及查询操作
插入
我们可以通过INNODB_FT_INDEX_CACHE来检查插入记录的分词:
mysql> insert into t1 values (NULL, 'hello, welcome to mysql world');
Query OK, 1 row affected (1.87 sec)
mysql> set global innodb_ft_aux_table = 'test/t1';
Query OK, 0 rows affected (0.00 sec)
mysql> select * from INNODB_FT_INDEX_CACHE;
+---------+--------------+-------------+-----------+--------+----------+
| WORD | FIRST_DOC_ID | LAST_DOC_ID | DOC_COUNT | DOC_ID | POSITION |
+---------+--------------+-------------+-----------+--------+----------+
| hello | 2 | 2 | 1 | 2 | 0 |
| mysql | 2 | 2 | 1 | 2 | 18 |
| welcome | 2 | 2 | 1 | 2 | 7 |
| world | 2 | 2 | 1 | 2 | 24 |
+---------+--------------+-------------+-----------+--------+----------+
4 rows in set (0.00 sec)
在插入一条记录时,对应的堆栈如下:
row_insert_for_mysql
|--> row_insert_for_mysql_using_ins_graph
|--> fts_trx_add_op // state = FTS_INSERT
在向原表上插入完成记录后,会去判断表上是否有全文索引(DICT_TF2_FTS),如果有的话,则将插入记录对应的doc id提取出来(fts_get_doc_id_from_row),并缓存到事务对象中。
删除
删除操作不会直接从全文索引里直接删除,因此依然可以从INNODB_FT_INDEX_CACHE中查到分词信息。
相关堆栈:
ha_innobase::delete_row
|--> row_update_for_mysql
|--> row_update_for_mysql_using_upd_graph
|--> row_fts_update_or_delete
|--> fts_trx_add_op // state = FTS_DELETE
更新
更新非全文索引列,不会修改FTS_DOC_ID列的值。如果更新了全文索引列,在InnoDB的实现是删除老的DOC,并插入新的DOC。
堆栈为:
ha_innobase::update_row
|--> row_update_for_mysql
|--> row_update_for_mysql_using_upd_graph
|--> row_fts_update_or_delete
|--> row_fts_do_update
|--> fts_trx_add_op // state = FTS_DELETE
|--> fts_trx_add_op // state = FTS_INSERT
可见所有DML的操作,都走接口函数fts_trx_add_op,划分为两种操作: FTS_INSERT及FTS_DELETE;当前事务涉及的doc id被存储到trx->fts_trx中,在执行SQL的过程中并没有更新全文索引,而是在事务提交时进行的。
在缓存操作时,维护了两个结构,一个是trx->fts_trx->savepoints,维护了事务全局的全文索引操作,另外一个是trx->fts_trx->last_stmt,维护的是当前SQL操作的doc id,前者在事务结束时处理,后者在SQL结束时清空。
查询
对于全文索引的查询,采用新的接口函数,分为两步:
根据检索词搜集符合条件的doc id。
JOIN::optimize |--> init_ftfuncs |--> Item_func_match::init_search |--> ha_innobase::ft_init_ext |--> fts_query在搜集满足查询条件的doc id时,首先读取DELETED表中记录的doc id,这些doc id随后被用做过滤。
根据搜集到的doc id,找到对应的记录,使用的索引是
dict_table_t::fts_doc_id_index,也就是建立在隐藏列FTS_DOC_ID上的唯一索引。sub_select |--> join_ft_read_first |--> ha_innobase::ft_init |--> ha_innobase::ft_read |--> join_ft_read_next |--> ha_innobase::ft_read
通常查询返回的结果是根据rank排序的,InnoDB的全文检索排序规则和sphinx类似,基于 BM25 和 TF-IDF算法。
rank的计算算法如下:
${IDF} = log10( ${total_records} / ${matching_records} ) // total_records表示总的行记录数,matching_records表示匹配到检索字的行记录数
${TF} 表示单词在文档中出现的次数
${rank} = ${TF} * ${IDF} * ${IDF}
IDF的计算参阅函数:fts_query_calculate_idf ranking计算:fts_query_calculate_ranking
如果使用多个单词匹配,则把各个单词各自的rank累加起来。官方博客有一篇文章专门对此进行了介绍。




