暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

mysql的rowid使用完了会怎样?

原创 大大刺猬 20小时前
25

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:'

image20260629162844783.png

怎么模拟使用完呢? 总不能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;

image20260629170914336.png

我们发现少了条数据:1

然后我们使用ibd2ql来看下

image20260629171053861.png

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

我们再看下dict_sys->row_id的值呢?

image20260629171341433.png

发现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字节上限又会怎么样呢?

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论