MySQL特性 - 表属性上的表达式 - 如何计算和存储

MySQL从5.7版本开始支持Generated Column, 并在最近的8.0版本中支持了Functional index, 以及default值支持表达式, 这几个特性都通过创建使用表达式进行描述的列来实现的。笔者之前满好奇这些表达式信息都是怎么存储的,本文主要记录了涉及到的相关函数,主要是做个笔记,不会深入解读。

本文以Generated Column为例进行描述,代码基于8.0.15

使用

我们创建一个简单的表,表上包含两种类型的generated column:物理存储和虚拟列;并在虚拟列上创建索引

root@information_schema 05:38:49>show create table test.t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `a` int(11) NOT NULL,
  `b` int(11) DEFAULT NULL,
  `c` int(11) DEFAULT NULL,
  `v1` int(11) GENERATED ALWAYS AS ((`a` + `b`)) VIRTUAL,
  `g1` int(11) GENERATED ALWAYS AS ((`a` * `v1`)) STORED,
  PRIMARY KEY (`a`),
  KEY `v1` (`v1`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1
1 row in set (0.00 sec)

root@information_schema 05:40:58>SELECT * FROM INNODB_VIRTUAL WHERE table_id = (SELECT TABLE_ID FROM INNODB_TABLES WHERE NAME LIKE 'test/t1')\G
*************************** 1. row ***************************
TABLE_ID: 1354
     POS: 65539
BASE_POS: 0
*************************** 2. row ***************************
TABLE_ID: 1354
     POS: 65539
BASE_POS: 1
2 rows in set (0.00 sec)

root@information_schema 05:41:04>

POS值实际上是一个encode的值,所以看起来很大,他包含了virtual column的序列和在所有列上的序列:

((nth virtual generated column for the InnoDB instance + 1) << 16)
+ the ordinal position of the virtual generated column

如上例,column v1, (0 + 1) << 16 + 3 = 65539

Generated column的表达式信息可以通过i_s表来查询:

root@information_schema 06:15:09>SELECT COLUMN_NAME, ORDINAL_POSITION,COLUMN_TYPE,EXTRA, GENERATION_EXPRESSION FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME LIKE 't1';
+-------------+------------------+-------------+-------------------+-----------------------+
| COLUMN_NAME | ORDINAL_POSITION | COLUMN_TYPE | EXTRA             | GENERATION_EXPRESSION |
+-------------+------------------+-------------+-------------------+-----------------------+
| a           |                1 | int(11)     |                   |                       |
| b           |                2 | int(11)     |                   |                       |
| c           |                3 | int(11)     |                   |                       |
| g1          |                5 | int(11)     | STORED GENERATED  | (`a` * `v1`)          |
| v1          |                4 | int(11)     | VIRTUAL GENERATED | (`a` + `b`)           |
+-------------+------------------+-------------+-------------------+-----------------------+
5 rows in set (0.00 sec)

相关代码

存储表达式

和其他元数据信息一样, 表达式也以字符串的形式存储到mysql库下面的columns表中,注意这个表是隐藏的,你只能通过information_schame.columns来查询。
系统掉定义在文件定义在文件sql/dd/impl/tables/columns.h中
ref: dd::Column_impl::store_attributes

读取表达式

通过dd接口(dd::Column_impl::restore_attributes),存储于系统表的表达式字符串被读取出来,并被存储到TABLE_SHARE的field成员的gcol_info中,类型为类型为Value_generator, 字符串存储于类型为Value_generator::expr_str中
ref: fill_column_from_dd

当会话打开自己的TABLE对象时,会基于上述的字符串信息构建item树,存储于自己的Value_generator的item树中。
ref: open_table_from_share --> unpack_value_generator

show create table时,通过TABLE对象,从generated column列的gcol_info中中构建出表达式信息
ref: store_create_info()

读和更新表达式

当generated column需要被更新时(TABLE::is_field_used_by_generated_columns),或者产生新的插入时,需要计算其结果值
ref: update_generated_write_fields()

当读取列时,如果virtual generated column, 需要去计算其真正的值. 当然如果virtual column上创建了innodb索引,实际上其值是被存储到物理索引上的,那么就无需去计算列值
ref: update_generated_read_fields()

InnoDB内计算表达式
当InnoDB选择使用virtual column上的索引来进行查询时,如果需要读取之前的版本,需要sec record和clust record检查是否匹配时(row_sel_sec_rec_is_for_clust_rec), 也需要基于clust record,根据表达式去构建出virtual column的值,这时候就需要去回调server层的计算函数,因为clust record中并不存在virtual column的值, 相应堆栈:

row_search_mvcc
|--> Row_sel_get_clust_rec_for_mysql::operator()
    |--> row_sel_sec_rec_is_for_clust_rec
        |--> innobase_get_computed_value
            |-->  handler::my_eval_gcolumn_expr

参考文档:

上一篇:MySQL8.0.14 - 新特性 - InnoDB Parallel Read简述


下一篇:[转贴]Gloomy对Windows内核的分析(研究CreateProcess)