MySQL8.0 - 新特性 - Functional Index

本文涉及的产品
RDS MySQL DuckDB 分析主实例,基础系列 4核8GB
RDS AI 助手,专业版
RDS MySQL DuckDB 分析主实例,集群系列 4核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.相关代码

相关实践学习
自建数据库迁移到云数据库
本场景将引导您将网站的自建数据库平滑迁移至云数据库RDS。通过使用RDS,您可以获得稳定、可靠和安全的企业级数据库服务,可以更加专注于发展核心业务,无需过多担心数据库的管理和维护。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。   相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情: https://www.aliyun.com/product/rds/mysql 
相关文章
|
7月前
|
SQL 监控 关系型数据库
MySQL事务处理:ACID特性与实战应用
本文深入解析了MySQL事务处理机制及ACID特性,通过银行转账、批量操作等实际案例展示了事务的应用技巧,并提供了性能优化方案。内容涵盖事务操作、一致性保障、并发控制、持久性机制、分布式事务及最佳实践,助力开发者构建高可靠数据库系统。
|
7月前
|
存储 关系型数据库 MySQL
介绍MySQL的InnoDB引擎特性
总结而言 , Inno DB 引搞 是 MySQL 中 高 性 能 , 高 可靠 的 存 储选项 , 宽泛 应用于要求强 复杂交易处理场景 。
284 15
|
7月前
|
关系型数据库 MySQL 数据库
MySql事务以及事务的四大特性
事务是数据库操作的基本单元,具有ACID四大特性:原子性、一致性、隔离性、持久性。它确保数据的正确性与完整性。并发事务可能引发脏读、不可重复读、幻读等问题,数据库通过不同隔离级别(如读未提交、读已提交、可重复读、串行化)加以解决。MySQL默认使用可重复读级别。高隔离级别虽能更好处理并发问题,但会降低性能。
249 0
|
SQL 安全 关系型数据库
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
事务是MySQL中一组不可分割的操作集合,确保所有操作要么全部成功,要么全部失败。本文利用SQL演示并总结了事务操作、事务四大特性、并发事务问题、事务隔离级别。
5451 56
【MySQL基础篇】事务(事务操作、事务四大特性、并发事务问题、事务隔离级别)
|
存储 关系型数据库 MySQL
MySQL中为什么要使用索引合并(Index Merge)?
通过这些内容的详细介绍和实际案例分析,希望能帮助您深入理解索引合并及其在MySQL中的
764 10
|
存储 Oracle 关系型数据库
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
从基本特性、技术选型、字段类型、事务提交方式、SQL语句、分页方法等方面对比Oracle和MySQL的区别。
3279 18
Oracle和MySQL有哪些区别?从基本特性、技术选型、字段类型、事务、语句等角度详细对比Oracle和MySQL
|
JSON 关系型数据库 MySQL
MySQL 8.0 新特性
MySQL 8.0 新特性
561 10
MySQL 8.0 新特性
|
关系型数据库 MySQL
mysql事务特性
原子性:一个事务内的操作统一成功或失败 一致性:事务前后的数据总量不变 隔离性:事务与事务之间相互不影响 持久性:事务一旦提交发生的改变不可逆
|
存储 关系型数据库 MySQL
MySQL 8.0特性-自增变量的持久化
【11月更文挑战第8天】在 MySQL 8.0 之前,自增变量(`AUTO_INCREMENT`)的行为在服务器重启后可能会发生变化,导致意外结果。MySQL 8.0 引入了自增变量的持久化特性,将其信息存储在数据字典中,确保重启后的一致性。这提高了开发和管理的稳定性,减少了主键冲突和数据不一致的风险。默认情况下,MySQL 8.0 启用了这一特性,但在升级时需注意行为变化。
312 1
|
SQL 安全 关系型数据库
MySQL8.2有哪些新特性?
【10月更文挑战第3天】MySQL8.2有哪些新特性?
450 2

相关产品

  • 云数据库 RDS MySQL 版
  • 推荐镜像

    更多