MySQL8.0 - 新特性 - Functional Index

本文涉及的产品
云数据库 RDS SQL Server,基础系列 2核4GB
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
云原生数据库 PolarDB 分布式版,标准版 2核8GB
简介: 前言 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.相关代码

相关实践学习
如何在云端创建MySQL数据库
开始实验后,系统会自动创建一台自建MySQL的 源数据库 ECS 实例和一台 目标数据库 RDS。
全面了解阿里云能为你做什么
阿里云在全球各地部署高效节能的绿色数据中心,利用清洁计算为万物互联的新世界提供源源不断的能源动力,目前开服的区域包括中国(华北、华东、华南、香港)、新加坡、美国(美东、美西)、欧洲、中东、澳大利亚、日本。目前阿里云的产品涵盖弹性计算、数据库、存储与CDN、分析与搜索、云通信、网络、管理与监控、应用服务、互联网中间件、移动服务、视频服务等。通过本课程,来了解阿里云能够为你的业务带来哪些帮助     相关的阿里云产品:云服务器ECS 云服务器 ECS(Elastic Compute Service)是一种弹性可伸缩的计算服务,助您降低 IT 成本,提升运维效率,使您更专注于核心业务创新。产品详情: https://www.aliyun.com/product/ecs
相关文章
|
8天前
|
存储 关系型数据库 MySQL
MySQL 8.0特性-自增变量的持久化
【11月更文挑战第8天】在 MySQL 8.0 之前,自增变量(`AUTO_INCREMENT`)的行为在服务器重启后可能会发生变化,导致意外结果。MySQL 8.0 引入了自增变量的持久化特性,将其信息存储在数据字典中,确保重启后的一致性。这提高了开发和管理的稳定性,减少了主键冲突和数据不一致的风险。默认情况下,MySQL 8.0 启用了这一特性,但在升级时需注意行为变化。
|
2月前
|
JSON 关系型数据库 MySQL
MySQL 8.0 新特性
MySQL 8.0 新特性
147 10
MySQL 8.0 新特性
|
2月前
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
502 18
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
1月前
|
SQL 安全 关系型数据库
MySQL8.2有哪些新特性?
【10月更文挑战第3天】MySQL8.2有哪些新特性?
36 2
|
3月前
|
算法 关系型数据库 MySQL
一天五道Java面试题----第七天(mysql索引结构,各自的优劣--------->事务的基本特性和隔离级别)
这篇文章是关于MySQL的面试题总结,包括索引结构的优劣、索引设计原则、MySQL锁的类型、执行计划的解读以及事务的基本特性和隔离级别。
|
3月前
|
SQL 算法 关系型数据库
(二十)MySQL特性篇:2022年的我们,必须要懂的那些数据库新技术!
 MySQL数据库从1995年诞生至今,已经过去了二十多个年头了,到2022.04.26日为止,MySQL8.0.29正式发行了GA版本,在此之前版本也发生了多次迭代,发行了大大小小N多个版本,其中每个版本中都有各自的新特性,所有版本的特性加起来,用一本书的篇幅也无法完全阐述清楚,因此本章主要会挑重点特性来讲,具体各版本的特性可参考MySQL官网的开发手册。
152 1
|
4月前
|
关系型数据库 MySQL 测试技术
MySQL 报错 ERROR 1709: Index column size too large
MySQL 报错 ERROR 1709: Index column size too large
212 4
|
5月前
|
关系型数据库 MySQL 测试技术
深入探索MySQL 8:隐藏索引与降序索引的新特性
深入探索MySQL 8:隐藏索引与降序索引的新特性
|
5月前
|
SQL JSON 关系型数据库
MySQL 8.0新特性?
【6月更文挑战第14天】MySQL 8.0新特性?
50 1
|
5月前
|
SQL 关系型数据库 MySQL

相关产品

  • 云数据库 RDS MySQL 版