暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

PolarDB · InnoDB 全文索引简介(二)

Z 2023-06-25
448

普通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结束时清空。

查询

对于全文索引的查询,采用新的接口函数,分为两步:

  1. 根据检索词搜集符合条件的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随后被用做过滤。

  2. 根据搜集到的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累加起来。官方博客有一篇文章专门对此进行了介绍。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论