MySQL 8.0中的invisible功能

发布时间 2023-08-06 16:44:09作者: abce

在本文中,将讨论mysql 8.0中的几个新功能和一个旧功能。
·invisible columns
·generated invisible primary keys
·invisible indexes

 

不可见的列(invisible columns)
不可见的列功能自8.0.23版开始实现的。
什么是不可见的列?它本质上是表中的普通列,有自己的名称和数据类型。它的处理和更新方式与其他普通列相同,唯一的区别是应用程序看不到它。换句话说,只有在select中明确涉及到它的情况下,才能访问它;否则,它就像一个不存在的列。

这个定义看起来很奇怪,通过一个实际使用案例,一切就都应该更清楚了。

假设应用代码中有select *查询。作为一名经验丰富的数据库开发人员,应该知道这类查询不应该出现在任何生产代码中。典型的问题是当你需要更改表的schema、添加或删除列,或者更糟糕的是在其他列中间添加新列时。获取到应用程序变量中的字段位置可能会完全破坏应用程序或引发意想不到的错误行为。这就是在应用程序中尽量避免使用select *的原因。

在这种情况下,如果需要避免更改应用程序代码以匹配新的表模式,可以将新列添加为不可见列,由于查询没有明确处理该列,因此不会将其返回给客户端。因此,应用程序不会出现故障或奇怪的行为。

你需要在列定义中使用invisible关键字。如果需要将列转换为可见列,则需要使用visible关键字。让我们来看一个例子。

为我们的文章创建一个表并插入几行:

> create table blogs ( id int auto_increment, ts timestamp default current_timestamp, article text, primary key(id));
query ok, 0 rows affected (0.03 sec)

> insert into blogs(article) values("this is first article"),("this is second article"),("this is third article");
query ok, 3 rows affected (0.01 sec)
records: 3  duplicates: 0  warnings: 0

> select * from blogs;
+----+---------------------+------------------------+
| id | ts                  | article                |
+----+---------------------+------------------------+
|  1 | 2023-08-06 15:29:56 | this is first article  |
|  2 | 2023-08-06 15:29:56 | this is second article |
|  3 | 2023-08-06 15:29:56 | this is third article  |
+----+---------------------+------------------------+

在某一时刻,我们决定必须在ts列之后向表中添加新的字段标题。为了避免由于select *和中间新添加的列导致应用程序无效,我们必须将title列创建为invisible。

> alter table blogs add column title varchar(100) invisible after ts;
query ok, 0 rows affected (0.02 sec)
records: 0  duplicates: 0  warnings: 0

给新加的列添加一些值:

> update blogs set title='title 1' where id=1; update blogs set title='title 2' where id=2; update blogs set title='title 3' where id=3;

现在表的定义如下:

>show create table blogs\G
*************************** 1. row ***************************
       Table: blogs
Create Table: CREATE TABLE `blogs` (
  `id` int NOT NULL AUTO_INCREMENT,
  `ts` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `title` varchar(100) COLLATE utf8mb4_general_ci DEFAULT NULL /*!80023 INVISIBLE */,
  `article` text COLLATE utf8mb4_general_ci,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci
1 row in set (0.00 sec)

可以看到新加的列,定义带有invisible关键字。

如果再次执行select *

>select * from blogs;
+----+---------------------+------------------------+
| id | ts                  | article                |
+----+---------------------+------------------------+
|  1 | 2023-08-06 15:29:56 | this is first article  |
|  2 | 2023-08-06 15:29:56 | this is second article |
|  3 | 2023-08-06 15:29:56 | this is third article  |
+----+---------------------+------------------------+
3 rows in set (0.00 sec)

可以看到,列没有返回。这样,在schema更改后,查询就不会失败。

如果想查看标题,则必须明确处理该字段:

> select id, ts, title, article from blogs;
+----+---------------------+---------+------------------------+
| id | ts                  | title   | article                |
+----+---------------------+---------+------------------------+
|  1 | 2023-08-06 15:29:56 | title 1 | this is first article  |
|  2 | 2023-08-06 15:29:56 | title 2 | this is second article |
|  3 | 2023-08-06 15:29:56 | title 3 | this is third article  |
+----+---------------------+---------+------------------------+

可以使用以下命令,将字段改成visible的;

> alter table blogs modify column title varchar(100) visible;

请记住,不可见列与其他常规列一样,因此可以随时读取和更新它们。有关不可见性的元数据可在information_schema中可以找到,而invisible/visible关键字会保留在binlog中,因此所有更改都会被正确的复制。

更多详情,请参阅文档:

https://dev.mysql.com/doc/refman/8.0/en/invisible-columns.html

 

 

生成不可见的主键
该功能已在mysql 8.0.30中部署;它是最新的功能。生成不可见的键(gipk)是一种特殊的隐形列,只适用于innodb表。

要知道,你可以创建没有显式主键的innodb表。这完全不是一种最佳做法,我们强烈建议你始终在表中创建显式主键。你可能还知道,如果你不提供主键,innodb会创建一个隐藏的主键,但gipk提供的新功能让主键变得可用并最终可见。相反,隐式创建的不可见的主键(这是一个长期存在的特性)既不能使用,也不可见。

归根结底,该功能可以迫使缺乏经验的用户使用显式主键的innodb表,即使是不可见的。

让我们看看它是如何工作的。

该功能默认是禁用的,因此mysql的行为将与过去一样。要启用gipk,必须设置以下动态系统变量(它具有全局和会话范围):

> set [persist] sql_generate_invisible_primary_key=on;

现在来创建一个没有显式主键的表:

> create table abce(name varchar(50));
query ok, 0 rows affected (0.03 sec)
show create table abce\G
*************************** 1. row ***************************
Table: abce
Create Table: CREATE TABLE `abce` (
`my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`my_row_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

自动创建按了不可见的主键,并命名为my_row_id。
注意:
1.不可见的主键(gipk)总是被命名为my_row_id,表中不能有列使用这个命名。
2.gipk的类型,总是bigint unsigned auto_increment的。

有趣的是,可以在查询中使用主键,并在显式处理的情况下查看主键,就像对不可见列所描述的那样。

> insert into abce values('jack'),('tom'),('helen');
query ok, 3 rows affected (0.00 sec)
records: 3  duplicates: 0  warnings: 0

> select my_row_id, name from abce;
+-----------+-------+
| my_row_id | name |
+-----------+-------+
| 1 | jack |
| 2 | tom |
| 3 | helen |
+-----------+-------+
3 rows in set (0.00 sec)

> select my_row_id, name from abce where my_row_id=2;
+-----------+------+
| my_row_id | name |
+-----------+------+
| 2 | tom |
+-----------+------+
1 row in set (0.00 sec)

显然,如果执行select *,主键将不会返回:

> select * from abce where my_row_id=2;
+------+
| name |
+------+
| tom |
+------+
1 row in set (0.00 sec)

你可以决定让它可见,并根据自己的喜好更改名称:

> alter table abce modify `my_row_id` bigint unsigned not null auto_increment visible;
query ok, 0 rows affected (0.01 sec)
records: 0  duplicates: 0  warnings: 0

> show create table abce\G
*************************** 1. row ***************************
Table: abce
Create Table: CREATE TABLE `abce` (
`my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
PRIMARY KEY (`my_row_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci
1 row in set (0.00 sec)

有关详细信息,请参阅文档:

https://dev.mysql.com/doc/refman/8.0/en/create-table-gipks.html

  

 

不可见索引
为了完成对不可见功能的概述,我们也来讨论一下不可见索引。这是在MySQL 8.0第一个版本中引入的最古老的功能。

简单来说,就是让优化器看不到某个索引,以便测试在该索引不存在的情况下查询的性能。总之,当索引不可见时,它会在对表执行任何DML语句(INSERT、UPDATE、DELETE、REPLACE)时被更新。

可以使用以下语句将索引设置为不可见,然后再恢复为可见:

ALTER TABLE mytable ALTER INDEX my_idx INVISIBLE;
ALTER TABLE mytable ALTER INDEX my_idx VISIBLE;

不可见索引可以测试查询的执行计划。它的最大优点是不需要删除索引。请记住,删除索引几乎是瞬时的,但重建索引却不是。根据表的大小,重建索引可能需要耗费大量时间,并导致服务器超载。作为替代方法,你也可以使用IGNORE INDEX()索引提示,但在这种情况下,你可能不得不在应用程序代码中的许多查询上添加索引提示。将索引设置为不可见,可以让你在很短时间内开始测试查询。还可以随时轻松地将其恢复为可见,而不会丢失任何更新。

 

注释:

·主键不可以改成不可见
·UNIQUE索引可以不可见,但唯一性检查照常执行
·有关索引不可见性的信息可在information_schema中找到
·索引不可见性可以被正确地复制

 

有关详细信息,请参阅文档:

https://dev.mysql.com/doc/refman/8.0/en/invisible-indexes.html

  

结论
在我看来,你不应该使用不可见列,因为作为最佳实践,你不应该在任何应用程序中部署SELECT *查询。无论如何,在某些紧急情况下,该功能可能有助于即时解决问题。但要记得修复代码,将不可见列变为可见列。肯定会更好。

GIPK也是如此。只要记得为表提供明确的主键,就不需要这个功能。总之,它可以让创建时没有主键的表有一个适当的主键,并可以轻松使用和可见。

那么不可见索引呢?这是一个非常简单的功能,但在运行测试时非常有用,尤其是在可以使用多个索引,而你又不确定优化程序是否选择了最佳执行计划的情况下。