You need to enable JavaScript to run this app.
导航
隐藏主键
最近更新时间:2025.03.03 12:12:30首次发布时间:2025.03.03 12:12:30

背景信息

无主键表

在 MySQL 数据库中,无主键表的创建会对高可用性架构引入较大风险:

  • Binlog 同步机制失效 当发生全表数据更新或范围删除操作时,基于 Row 格式的 binlog 在从库回放过程中,由于缺乏聚簇索引,每条 binlog 事件都需要执行全表扫描定位数据。实测发现,百万级数据表的这类操作会导致主备同步延迟呈指数级增长。

  • 存储引擎特性冲突 InnoDB 引擎的聚簇索引特性要求必须有唯一行标识。当表缺失显式主键时:

    • 引擎自动生成的隐式 ROW_ID 不具备索引可见性。

    • 二级索引叶子节点无法精确定位数据页。

    • 每次 DML 操作实质上都退化为全表遍历。

  • 高可用链路雪崩效应 无主键表引发的延迟具有自我恶化特性:

    • 主库持续产生新 binlog 事件。

    • 从库因全表扫描无法及时消费事件队列。

    • 最终导致复制中断。

    • 故障切换时出现数据不一致风险。

解决办法

  • 事后处理 - 重做备库

    在无主键表已经产生后,在不改变表结构的情况下处理的办法,办法是设置主从延迟告警,并人工查看是否为无主键表造成,并通过工单重做备库,带来了额外的人力负担,不可持续。

  • 事前处理 - 隐藏主键

    MySQL 社区 8.0.30 带来了隐藏主键(GIPK, i.e. Generated Invisible Primary Key)特性,可以识别 CREATE TABLE 建表语句中的无主键表,通过 MySQL 的「隐藏列」属性,给表添加一个用户不可见的主键索引。这样既可以对用户使用不造成影响,又可以解决无主键表的问题,让新建表不存在无主键的情况。

云数据库 MySQL 版 GPIK 能力简介

云数据库 MySQL 版在 8.0 社区版的基础上对社区的隐藏主键进行了若干优化,并在 MySQL 5.7 通过唯一索引实现了类似 GIPK 的功能。云数据库 MySQL 版在 MySQL 8.0 和 MySQL 5.7 中提供的隐藏主键能力对比如下。

GIPK属性MySQL 8.0MySQL 5.7

KEY类型

主键索引(Primary Key)
bigint(20), auto_increment

唯一索引(Unique Index)
bigint(20), auto_increment

主键列名称my_row_idbyte_rds_ai_uk_row_id
创建 GIPK 条件CREATE TABLE 语句无主键CREATE TABLE 语句无主键或唯一键
用户新增 PK 后,是否自动DROP 主键列
用户 Select * 查询是否能选中 GIPK
控制参数(session)sql_generate_invisible_primary_keyloose_sql_generate_invisible_primary_key
展示 GIPK 参数show_gipk_in_create_table_and_information_schemaloose_show_gipk_in_create_table_and_information_schema

使用方法

打开隐藏主键

修改实例参数为 ON

  • MySQL 8.0:sql_generate_invisible_primary_key

  • MySQL 5.7:loose_sql_generate_invisible_primary_key

说明

  • 以上两个参数非开放的可修改参数。如需修改,请提交工单联系技术支持。
  • 在内核小版本为 20241215 及更新版本的实例中,以上两个参数的值默认为 ON

查看隐藏主键

MySQL 5.7

会话级别打开 loose_show_gipk_in_create_table_and_information_schema 参数后可以在 show create table 中看到隐藏主键 byte_rds_ai_uk_row_id

mysql> set loose_show_gipk_in_create_table_and_information_schema=on;

mysql> show variables like '%gipk%';
+--------------------------------------------------------+-------+
| Variable_name                                          | Value |
+--------------------------------------------------------+-------+
| loose_show_gipk_in_create_table_and_information_schema | ON    |
+--------------------------------------------------------+-------+
1 row in set (0.01 sec)

mysql > show create table tt1;
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                               |
+-------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tt1   | CREATE TABLE tt1 (
       `byte_rds_ai_uk_row_id` bigint unsigned NOT NULL AUTO_INCREMENT COMMENT 'BYTE_RDS_INVISIBLE',
       `f1` int DEFAULT NULL,
       `f2` int DEFAULT NULL,
      UNIQUE KEY (`byte_rds_ai_uk_row_id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

MySQL 8.0

会话级别打开 show_gipk_in_create_table_and_information_schema 参数后可以在 show create table 中看到隐藏主键 my_row_id

mysql> set show_gipk_in_create_table_and_information_schema=on;

mysql> show variables like '%gipk%';
+--------------------------------------------------+-------+
| Variable_name                                    | Value |
+--------------------------------------------------+-------+
| show_gipk_in_create_table_and_information_schema | ON    |
+--------------------------------------------------+-------+
1 row in set (0.01 sec)

mysql> show create table t00;
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| Table | Create Table                                                                                                                                                                                                                  |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| t00   | CREATE TABLE `t00` (
  `my_row_id` bigint unsigned NOT NULL AUTO_INCREMENT /*!80023 INVISIBLE */,
  `id` int DEFAULT NULL,
  `c` varchar(16) DEFAULT NULL,
  PRIMARY KEY (`my_row_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 |
+-------+-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
1 row in set (0.01 sec)

DDL 兼容性一览表

打开 GIPK 后,云数据库 MySQL 版对 DDL 的支持情况如下表所示。

说明

内核小版本为 20241015 或更新版本。

CREATE TABLE

描述

DDL 示例

MySQL 5.7

MySQL 8.0

建表 DDL 中有用户定义的 auto_increment列(不定义为索引)

create table gipk_test(i int auto_increment, c varchar(255));

不允许。
ERROR 1075
原生行为,auto inc 必须为索引

建表 DDL 中有用户定义的 auto_increment 列(定义为索引)

create table gipk_test(i int auto_increment unique key, c varchar(255));

允许。

建表 DDL 中使用 GIPK 预留列名,不作为隐藏主键(非 bigint NOT NULL AUTO_INCREMENT

create table gipk_test(`my_row_id` int auto_increment unique key, c varchar(255));

不允许。
不允许将byte_rds_ai_uk_row_id 作为列名。

允许,但输出告警
跳过 GIPK 创建,my_row_id 为普通列。

使用 GIPK 相关 COMMENT

create table gipk_test(i int COMMENT 'Implicit Primary Key by RDS', c varchar(255));

允许。

创建无主键分区表 DDL

CREATE TABLE gipk_test (
    sale_id INT NOT NULL,
    sale_date DATE NOT NULL,
    customer_id INT NOT NULL,
    amount DECIMAL(10,2) NOT NULL
)
PARTITION BY RANGE (YEAR(sale_date)) (
    PARTITION p2020 VALUES LESS THAN (2021),
    PARTITION p2021 VALUES LESS THAN (2022),
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION p2023 VALUES LESS THAN (2024)
);

允许。跳过 GIPK 创建。

binlog_format=statment 下
执行 Create table select

set binlog_format=statement;
create table gipk_test from select * from t_gipk;

不允许。无法修改为 SBR。

ALTER TABLE

示例中使用的表结构

  • GIPK 表: create table gipk_test(i int, c varchar(255));
  • 普通主键表:create table gipk_test(i int auto_increment primary key, c varchar(255));

描述

DDL 示例

MySQL 5.7

MySQL 8.0

给 GIPK 主键表添加主键

alter table gipk_test add column id int primary key;

允许。

给无主键表添加了自增列

alter table gipk_test add column id int auto_increment;

不允许。
ERROR 1075 AUTO INC 必须为索引,原生行为。

给无主键表添加了自增列并作为 KEY

alter table gipk_test add column id int auto_increment key;

允许。

用户给无主键表添加了自增列并作为 UNIQUE KEY

alter table gipk_test add column id int auto_increment unique key;

允许。

Drop 表中的隐藏主键列

alter table gipk_test drop column `my_row_id`;

允许。

Drop 表中的主键列

create table gipk_test(i int primary key, c varchar(255));
alter table gipk_test drop column i;

允许。
变为无主键表。

Drop 表中的主键

create table gipk_test(i int primary key, c varchar(255));
alter table gipk_test drop primary key;

允许。
变为无主键表。

Drop 表中的隐藏主键

alter table gipk_test drop key `my_row_id`;

允许。

将表中的一列升级为主键

create table gipk_test(f1 int , f2 int , f3 varchar(16) ,  c varchar(255));

alter table gipk_test add primary key (f1);

允许。

GIPK 表将 GIPK 作为分区键

CREATE TABLE gipk_test (f1 INT);
ALTER TABLE gipk_test PARTITION BY KEY(byte_rds_ai_uk_row_id) PARTITIONS 10;

不允许。
隐藏列不应作为分区键,DDL 报错。

GIPK 表将其他列作为分区键

CREATE TABLE gipk_test (f1 INT);
ALTER TABLE gipk_test PARTITION BY KEY(f1) PARTITIONS 10;

允许。
同时 DROP GIPK 列。

告警处理

关于隐藏主键相关的告警处理,请参见如何处理隐藏主键相关报错