6月快结束了, 赶紧水一篇
导读
创建表的时候, 我们强烈建议要设置主键/唯一索引, 没得合适的字段,就加个自增字段也行.
我们知道, 如果建表时,未指定主键,则mysql会自动添加一个自增的隐藏字段rowid来作为主键. 该rowid全局共用(dict_sys->row_id),使用6字节(存储)表示,也就是能表示281474976710656(281万亿)个值,不太可能达到上限,毕竟mac地址也是6字节(48bit).
但是,既然有上限,且全局共用, 就总是有使用完的情况的, 那么使用完了会怎么样?
模拟rowid使用完
rowid是无法使用sql查询出来的, 怎么观察呢?
我们之前写的ibd2sql-v1.x版本有debug模式, 是能看到所有隐藏字段的:rowid,trxid,rollptr.
python3 main.py /data/mysql_5744/mysqldata/db1/t20260629_01.ibd --debug --sql | grep -E 'data:|ROW_ID:'

怎么模拟使用完呢? 总不能insert 281万亿行数据吧… 既然是全局的,那么通常就有相关的’全局变量’: dict_sys->row_id
也就是可以使用如下命令查看和修改
# 查看
gdb -p `pidof mysqld` --batch --ex 'print dict_sys->row_id'
# 修改
gdb -p `pidof mysqld` --batch --ex 'set dict_sys->row_id=1'
准备测试表
先初始化rowid为1,方便后续验证
准备测试数据:
create table db1.t1(c1 int);
create table db1.t2(c2 int);
insert into db1.t1 values(1),(2);
insert into db1.t2 values(1),(2);
insert into db1.t1 values(3);
观察此时的rowid分布情况
17:03:55 [root@ddcw21 ibd2sql-ibd2sql-v1.x]#python3 main.py /data/mysql_5744/mysqldata/db1/t1.ibd --debug --sql | grep -E 'data:|ROW_ID:' [2026-06-29 17:03:57] [DEBUG] ROW_ID:1 [2026-06-29 17:03:57] [DEBUG] 145 ----> 149 data:1 bdata:b'\x80\x00\x00\x01' [2026-06-29 17:03:57] [DEBUG] ROW_ID:2 [2026-06-29 17:03:57] [DEBUG] 174 ----> 178 data:2 bdata:b'\x80\x00\x00\x02' [2026-06-29 17:03:57] [DEBUG] ROW_ID:5 [2026-06-29 17:03:57] [DEBUG] 203 ----> 207 data:3 bdata:b'\x80\x00\x00\x03' 17:03:57 [root@ddcw21 ibd2sql-ibd2sql-v1.x]# 17:03:59 [root@ddcw21 ibd2sql-ibd2sql-v1.x]#python3 main.py /data/mysql_5744/mysqldata/db1/t2.ibd --debug --sql | grep -E 'data:|ROW_ID:' [2026-06-29 17:04:04] [DEBUG] ROW_ID:3 [2026-06-29 17:04:04] [DEBUG] 145 ----> 149 data:1 bdata:b'\x80\x00\x00\x01' [2026-06-29 17:04:04] [DEBUG] ROW_ID:4 [2026-06-29 17:04:04] [DEBUG] 174 ----> 178 data:2 bdata:b'\x80\x00\x00\x02'
我们发现: t1.c1=1时,rowid为1; t1.c1=2时,rowid为2; t2.c2=1时,rowid=3,t2.c2=2时,rowid=5;t1.c1=3时,rowid为5.
即rowid确实是全局共用且自增的.
使用gdb设置rowid的全局值达到上限
然后我们使用gdb来设置下rowid的值为: 281474976710654
gdb -p `pidof mysqld` --batch --ex 'set dict_sys->row_id=281474976710654'
然后我们再往t1表插入4条数据看下:
insert into db1.t1 values(4);
insert into db1.t1 values(5);
insert into db1.t1 values(6);
insert into db1.t1 values(7);
select * from db1.t1;

我们发现少了条数据:1
然后我们使用ibd2ql来看下

我们看到:c1为5时,row_id达到了上限281474976710655, 当c1=6时,row_id变成了0, 当c1=7时,row_id变成了1,并把之前的c1=1覆盖了.
我们再看下dict_sys->row_id的值呢?

发现dict_sys->row_id为281474976710658, 已经超过了6字节,也没有溢出. 查看源码发现row_id的类型是ib_uint64_t, 即该变量使用8字节表示, 但page中只使用后6字节…
总结
mysql的row_id是全表共用的, 即对所有表都唯一. 当rowid达到上限(6字节)后,会从0开始,并覆盖原来的数据(无告警,无报错, 就单纯的丢掉数据).
row_id在数据字典(dict_sys_t)使用8字节表示, 但在page中只使用6字节. 问题来了,当row_id在内存中到8字节上限又会怎么样呢?




