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

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: MySQL从5.7版本开始支持Generated Column, 并在最近的8.0版本中支持了Functional index, 这两个特性都通过创建使用表达式进行描述的列来实现的。笔者之前满好奇这些表达式信息都是怎么存储的,本文主要记录了涉及到的相关函数,主要是做个笔记,不会深入解读。

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

参考文档:

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助 &nbsp; &nbsp; 相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
Oracle 关系型数据库 MySQL
【mysql】—— 表的内连和外连
【mysql】—— 表的内连和外连
|
1月前
|
存储 关系型数据库 MySQL
【mysql】—— 表的增删改查
【mysql】—— 表的增删改查
|
1月前
|
存储 缓存 关系型数据库
MySQL的varchar水真的太深了——InnoDB记录存储结构
varchar(M) 能存多少个字符,为什么提示最大16383?innodb怎么知道varchar真正有多长?记录为NULL,innodb如何处理?某个列数据占用的字节数非常多怎么办?影响每行实际可用空间的因素有哪些?本篇围绕innodb默认行格式dynamic来说说原理。
828 6
MySQL的varchar水真的太深了——InnoDB记录存储结构
|
1月前
|
分布式计算 DataWorks 关系型数据库
DataWorks支持将ODPS表拆分并回流到MySQL的多个库和表中
【2月更文挑战第14天】DataWorks支持将ODPS表拆分并回流到MySQL的多个库和表中
56 8
|
1月前
|
SQL 关系型数据库 MySQL
Mysql基础第二十四天,创建表和操纵表
Mysql基础第二十四天,创建表和操纵表
29 0
Mysql基础第二十四天,创建表和操纵表
|
16天前
|
存储 SQL 关系型数据库
【MySQL】4. 表的操作
【MySQL】4. 表的操作
19 0
|
1月前
|
SQL 关系型数据库 MySQL
【MySQL】— —熟练掌握用SQL语句实现数据库和基本表的创建。熟练掌握MySQL的安装、客户端登录方法;熟练掌握MySQL的编码、数据类型等基础知识;掌握实体完整性的定义和维护方法、掌握参照完整性
【MySQL】— —熟练掌握用SQL语句实现数据库和基本表的创建。熟练掌握MySQL的安装、客户端登录方法;熟练掌握MySQL的编码、数据类型等基础知识;掌握实体完整性的定义和维护方法、掌握参照完整性
99 1
|
15天前
|
存储 关系型数据库 MySQL
MySQL数据库性能大揭秘:表设计优化的高效策略(优化数据类型、增加冗余字段、拆分表以及使用非空约束)
MySQL数据库性能大揭秘:表设计优化的高效策略(优化数据类型、增加冗余字段、拆分表以及使用非空约束)
|
15天前
|
缓存 关系型数据库 MySQL
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
MySQL 查询优化:提速查询效率的13大秘籍(索引设计、查询优化、缓存策略、子查询优化以及定期表分析和优化)(中)
|
16天前
|
关系型数据库 MySQL
【MySQL】12. 表的内连和外连(重点)
【MySQL】12. 表的内连和外连(重点)
12 0

相关产品

  • 云数据库 RDS MySQL 版