MySQL Innodb 引擎采用的是 IOT(索引组织表)存储方式,主键的重要性就不言而喻。在早期版本用户如果没有显式指定主键,会自动生成隐藏主键 row_id 来组织 B+ 树,隐藏主键 row_id 只会作用于 MVCC、Redo 和 Undo 等内部机制,无法在复制模块中使用。一些大数据组件生成的表结构,往往都没有主键设计,会出现全表扫描回放问题,带来非常大的主备延迟。
而且 MySQL 一些周边软件 gh-ost、DTS 服务等,都有依赖主键设计,没有主键会降低数据库的可维护性。
Generated Invisible Primary Keys 简称 GIPK,是 2022 年 7 月 16 日 MySQL 发布 8.0.30 版本中的新特性,仅适用于 Innodb 引擎。
GIPK 特性,通过 sql_generate_invisible_primary_key 参数来控制是否开启,默认是关闭状态。
下面的实验,在 GIPK 禁用的状态下,创建一张 auto_0 表,随后开启 GIPK 特性,然后创建 auto_1 表。
mysql> SELECT @@sql_generate_invisible_primary_key;
+--------------------------------------+
| @@sql_generate_invisible_primary_key |
+--------------------------------------+
| 0 |
+--------------------------------------+
1 row in set (0.00 sec)
mysql> CREATE TABLE auto_0 (c1 VARCHAR(50), c2 INT);
Query OK, 0 rows affected (0.02 sec)
mysql> SET sql_generate_invisible_primary_key=ON;
Query OK, 0 rows affected (0.00 sec)
mysql> SELECT @@sql_generate_invisible_primary_key;
+--------------------------------------+
| @@sql_generate_invisible_primary_key |
+--------------------------------------+
| 1 |
+--------------------------------------+
1 row in set (0.00 sec)
mysql> CREATE TABLE auto_1 (c1 VARCHAR(50), c2 INT);
Query OK, 0 rows affected (0.04 sec)
实验 show create table 对比两张表结构是否有不同:
发现在开启 GIPK 情况下,未指定主键创建表,MySQL 会帮助我们创建名为 my_row_id 主键:
my_row_id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT INVISIBLE PRIMARY KEY
向表 auto_1 表中插入数据:
insert into auto_1 values('a', 1),('b', 2),('c', 3),('d', 4),('e', 5);
执行查询,发现默认是不可见的:
select * from auto_1;
-- 结果:
+------+------+
| c1 | c2 |
+------+------+
| a | 1 |
| b | 2 |
| c | 3 |
| d | 4 |
| e | 5 |
+------+------+
除非用户主动指定该字段:
select my_row_id, c1, c2 from auto_1;
-- 结果:
+-----------+------+------+
| my_row_id | c1 | c2 |
+-----------+------+------+
| 1 | a | 1 |
| 2 | b | 2 |
| 3 | c | 3 |
| 4 | d | 4 |
| 5 | e | 5 |
+-----------+------+------+
查询元数据库,是可以查到相关信息:
select * from information_schema.COLUMNS where TABLE_NAME='auto_1' and COLUMN_KEY = 'PRI'\G
开启 GIPK 后,默认会在 show create table 和 information_schema 等元数据库信息中看到,用户可以通过下方参数实现隐藏 my_row_id 的元数据信息,默认不隐藏:
select @@show_gipk_in_create_table_and_information_schema;
可通过 ALTER 语句将 my_row_id 不可见属性进行修改:
ALTER TABLE `auto_1` ALTER column my_row_id set visible;
ALTER TABLE `auto_1` ALTER column my_row_id set invisible;
看到 GIPK 特性,我的第一反应是 “能否解决无主键表带来的延迟问题?” 下面通过解析 Binlog 进行验证:
SET @@SESSION.GTID_NEXT= '4f4ab2eb-b0fd-11ed-b9e2-fa7581637800:15711'/*!*/;
# at 15439012
#230223 13:45:44 server id 553306 end_log_pos 15439315 CRC32 0x82a51a79 Query thread_id=201180 exec_time=0 error_code=0
SET TIMESTAMP=1677131144/*!*/;
SET @@session.character_set_client=255,@@session.collation_connection=255,@@session.collation_server=45/*!*/;
CREATE TABLE `auto_1` (
`my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */,
`c1` varchar(50) COLLATE utf8mb4_general_ci DEFAULT NULL,
`c2` int DEFAULT NULL,
PRIMARY KEY (`my_row_id`)
)
-- 插入
### INSERT INTO `test`.`auto_1`
### SET
### @1=1
### @2='a'
### @3=1
### INSERT INTO `test`.`auto_1`
### SET
### @1=2
### @2='b'
### @3=2
### INSERT INTO `test`.`auto_1`
### SET
### @1=3
### @2='c'
### @3=3
### INSERT INTO `test`.`auto_1`
### SET
### @1=4
### @2='d'
### @3=4
主库在开启 GIPK 后,创建表后 Binlog 中也会有创建 my_row_id 语句,说明可以解决该问题。
需要注意的是,一定要保证主备库的字段 invisible/visible 属性是相同的。
若主库没有开启 GIPK 而从库开启,那么 GIPK 是否会生效?
经测试,主库未开启 GIPK 备库开启 GIPK,主库上创建无主键表,备库不会主动为其创建主键。
mysqldump 8.0.30 版本提供 --skip-generated-invisible-primary-key 参数。
如果不指定该参数,导出的 SQL 就会包含 my_row_id,如果指定则会忽略 my_row_id 字段。
my_row_id 已经占用表中唯一的 AUTO_INCREMENT 属性,无法再次指定自增属性。
ERROR 1075 (42000): Incorrect table definition; there can be only one auto column and it must be defined as a key
my_row_id 已经成为关键字,无法使用。
ERROR 4108 (HY000): Failed to generate invisible primary key. Column ‘my_row_id’ already exists.
参考文档:
【1】https://dev.mysql.com/doc/relnotes/mysql/8.0/en/news-8-0-30.html#mysqld-8-0-30-gipk