MySQL8.0 - 新特性 - Functional Index

本文涉及的产品
云原生数据库 PolarDB MySQL 版,Serverless 5000PCU 100GB
云原生数据库 PolarDB 分布式版,标准版 2核8GB
云数据库 RDS MySQL Serverless,0.5-2RCU 50GB
简介: 前言 MySQL8.0.13版本开始支持使用表达式或函数来作为索引键值,这使得索引的定义更加灵活,一些运算可以直接转移到索引上去。 实际上在之前的版本,我们也可以通过在generated column上创建索引的方式来实现类似功能, root@test 05:20:44>CREATE TABLE .

前言

MySQL8.0.13版本开始支持使用表达式或函数来作为索引键值,这使得索引的定义更加灵活,一些运算可以直接转移到索引上去。 实际上在之前的版本,我们也可以通过在generated column上创建索引的方式来实现类似功能,

root@test 05:20:44>CREATE TABLE t1 (a INT, b INT, c INT, PRIMARY KEY((a + b)));
ERROR 3756 (HY000): The primary key cannot be a functional index
root@test 05:21:12>CREATE TABLE t1 (a INT, b INT, c INT, KEY((a + b)));
Query OK, 0 rows affected (0.13 sec)

root@test 05:21:29>ALTER TABLE t1 ADD KEY((a+b), (a-b));
Query OK, 0 rows affected (0.20 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@test 05:22:10>ALTER TABLE t1 ADD KEY((a+b), a);
Query OK, 0 rows affected (0.20 sec)
Records: 0  Duplicates: 0  Warnings: 0

root@test 12:08:58>SHOW INDEX FROM t1;
+-------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+-------------+
| Table | Non_unique | Key_name           | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | Visible | Expression  |
+-------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+-------------+
| t1    |          1 | functional_index   |            1 | NULL        | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | (`a` + `b`) |
| t1    |          1 | functional_index_2 |            1 | NULL        | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | (`a` + `b`) |
| t1    |          1 | functional_index_2 |            2 | NULL        | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | (`a` - `b`) |
| t1    |          1 | functional_index_3 |            1 | NULL        | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | (`a` + `b`) |
| t1    |          1 | functional_index_3 |            2 | a           | A         |           0 |     NULL |   NULL | YES  | BTREE      |         |               | YES     | NULL        |
+-------+------------+--------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+---------+-------------+
5 rows in set (0.01 sec)

限制

  • 主键上无法建functional index
  • 可以混合普通key和functional key
  • 表达式需要定义在括号()内,但类似这样INDEX ((col1), (col2))定义是不允许的
  • functional key不允许选做外键
  • Spatial/Fulltext index不允许functional key
  • 如果列被某个functional index引用,需要先删除索引,才能删列
  • 不允许直接引用列前缀,但可以通过函数substring/cast来workaround
  • 对于一个unique的functional index,不能隐式转换为表的主键
root@test 10:27:48>CREATE TABLE tb (col longtext, key(col));
ERROR 1170 (42000): BLOB/TEXT column 'col' used in key specification without a key length
root@test 10:28:15>CREATE TABLE tb (col longtext, key((substring(col, 1, 10)));
Query OK, 0 rows affected (0.12 sec)

实现思路

该特性的实现思路是针对索引上被括号包围的表达式建立隐藏的虚拟列(virtual generated column),并在虚拟列上创建索引,这些功能早已经存在了,因此这个worklog主要做了几件事情:

语法支持

扩展新的语法,允许在创建索引时使用表达式
索引内的表达式被翻译成创建列的操作(Create_field), 索引上每个表达式各对应一个虚拟列

自动创建虚拟列

这个功能的核心就是讲索引创建引用的表达式转换成虚拟列并隐藏处理,因此在创建索引之前要进行预处理,入口函数

mysql_prepare_create_table
    -->add_functional_index_to_create_list

在获得表达式后,需要根据表达式来推导列类型,由于代码中已经有为create table as select推导列类型, 所以这里复用了其中的代码,单独抽出来函数create_table_from_items中的代码refactor到Create_field *generate_create_field

虚拟列的命名为计算 md5(index name + key part number), 参考函数: make_functional_index_column_name
如上例:

root@test 12:10:02>SET SESSION debug="+d,show_hidden_columns";
Query OK, 0 rows affected (0.00 sec)

root@test 12:12:54>show create table t1\G
*************************** 1. row ***************************
       Table: t1
Create Table: CREATE TABLE `t1` (
  `a` int(11) DEFAULT NULL,
  `b` int(11) DEFAULT NULL,
  `c` int(11) DEFAULT NULL,
  `3bb8c14d415110ac3b3c55ce9108ae2d` bigint(12) GENERATED ALWAYS AS ((`a` + `b`)) VIRTUAL,
  `0d1cbc68e8957783288d2b71268047c7` bigint(12) GENERATED ALWAYS AS ((`a` + `b`)) VIRTUAL,
  `0d8d996e0f781cf4e749dfa71efc17ba` bigint(12) GENERATED ALWAYS AS ((`a` - `b`)) VIRTUAL,
  `e0a812eddbaed00becd72bf920eccab8` bigint(12) GENERATED ALWAYS AS ((`a` + `b`)) VIRTUAL,
  KEY `functional_index` (((`a` + `b`))),
  KEY `functional_index_2` (((`a` + `b`)),((`a` - `b`))),
  KEY `functional_index_3` (((`a` + `b`)),`a`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

隐藏虚拟列

dd::Column::enum_hidden_type m_hidden: 增加该属性,用于区分是用户定义的generated column还是自动生成的。据此判断该列是否能够对用户可见或者是否可以被删除等等

  enum class enum_hidden_type {
    /// The column is visible (a normal column)
    HT_VISIBLE = 1,
    /// The column is completely invisible to the server
    HT_HIDDEN_SE = 2,
    /// The column is visible to the server, but hidden from the user.
    /// This is used for i.e. implementing functional indexes.
    HT_HIDDEN_SQL = 3
  };

增加两个接口函数来判断是否是隐藏列:

  1. is_field_for_functional_index(), 用于:
"ALTER TABLE tbl DROP COLUMN;" 当列是隐藏列时,会抛出错误, ref: is_field_used_by_functional_index
Item_field::print() : 打印列的表达式而非列名, ref: get_field_name_or_expression
  1. is_hidden_from_user()
"INSERT INTO tbl;" without a column list, ref: insert_fields, Sql_cmd_insert_base::prepare_inner()
"SELECT * FROM tbl;"
"SHOW CREATE TABLE tbl;" and "SHOW FIELDS FROM tbl;",  ref : store_create_info

DDL:

prepare_create_field()
-当尝试加一个和隐藏列相同名字的列时,抛出错误

创建索引名:
当用户未指定列名时,server会自动创建列名,对于functional index和普通索引不太一样:因为列名是索引名和在索引上的key number产生的hash值,因此必须在生成虚拟列之前产生索引名.

mysql_alter_table:

  • 当删除索引时,相应的隐藏虚拟列也必须删除, ref: handle_drop_functional_index
  • 当rename索引名时,隐藏列名也必须重新计算并重命名, ref: handle_rename_functional_index

报错

Functional_index_error_handler: 隐藏虚拟列上的错误或warning信息, 转换成索引错误信息

参考文档

1.WL#1075: Add support for functional indexes
2.官方文档
3.相关代码

相关实践学习
基于CentOS快速搭建LAMP环境
本教程介绍如何搭建LAMP环境,其中LAMP分别代表Linux、Apache、MySQL和PHP。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
3月前
|
存储 SQL 关系型数据库
MySQL相关(五)- 事务四大特性及隔离级别的详细介绍
MySQL相关(五)- 事务四大特性及隔离级别的详细介绍
43 0
|
2月前
|
SQL 关系型数据库 MySQL
Mysql事务隔离级别和锁特性
Mysql事务隔离级别和锁特性
|
26天前
|
存储 缓存 关系型数据库
MySQL事务的四大特性是如何保证的
在MySQL数据库中还有一种二进制日志,其用来基于时间点的还原及主从复制。从表面上来看其和重做日志非常相似,都是记录了对于数据库操作的日志。但是,从本质上来看有着非常大的不同。
11 1
|
1月前
|
SQL 关系型数据库 MySQL
深入理解MySQL事务特性:保证数据完整性与一致性
深入理解MySQL事务特性:保证数据完整性与一致性
87 1
|
1月前
|
存储 安全 关系型数据库
MySQL 临时表的用法和特性
MySQL 临时表的用法和特性
|
1月前
|
关系型数据库 MySQL 测试技术
数据库专家带你体验PolarDB MySQL版 Serverless的极致弹性特性!
本次基于阿里云瑶池数据库解决方案体验馆,带你体验PolarDB MySQL Serverless形态下的性能压测环境,基于可选择的标准压测工具进行压测,构造弹性场景进行压测,实时动态展示弹性能力、价格和性价比结果,压测环境可开放定制修改、可重复验证。参与活动即有机会获得鼠标、小米打印机、卫衣等精美礼品。
数据库专家带你体验PolarDB MySQL版 Serverless的极致弹性特性!
|
2月前
|
关系型数据库 MySQL 测试技术
数据库专家带你体验PolarDB MySQL版 Serverless的极致弹性特性
本次基于阿里云瑶池数据库解决方案体验馆,带你体验PolarDB MySQL Serverless形态下的性能压测环境,基于可选择的标准压测工具进行压测,构造弹性场景进行压测,实时动态展示弹性能力、价格和性价比结果,压测环境可开放定制修改、可重复验证。参与活动即有机会获得鼠标、小米打印机、卫衣等精美礼品。
|
2月前
|
关系型数据库 MySQL 数据库
MySQL事务得四大特性以及实现原理
MySQL事务得四大特性以及实现原理
|
2月前
|
存储 SQL 关系型数据库
三高Mysql - Mysql特性和未来发展
三高Mysql - Mysql特性和未来发展
40 0
|
3月前
|
SQL Oracle 关系型数据库
MySQL事务 【事务操作丨事务四大特性丨事务隔离级别丨事务原理】
MySQL事务 【事务操作丨事务四大特性丨事务隔离级别丨事务原理】
45 0

相关产品

  • 云数据库 RDS MySQL 版