背景
问题:
有一张表,里面有 id
自增主键,当 insert
了 17 条记录之后,删除了第 15 16 17 条记录,再把 MySQL 重启,再 insert
一条记录,这条记录的 id
是 18 还是 15 ?
答案:
MySQL 8.0 之前
- 如果是
MyISAM
表,数据库重启后,id
值为 18 - 如果是
InnoDB
表,数据库重启后,id
值为 15
MySQL 8.0 开始
- 如果是
MyISAM
表,数据库重启后,id
值为 18 - 如果是
InnoDB
表,数据库重启后,id
值为 18
原理
不同存储引擎自增主键实现机制不同,同一存储引擎不通版本实现机制不同。
MyISAM:自增主键的最大 id
记录在数据文件里,重启后不会消失。
InnoDB
MySQL 5.7 及之前版本
,自增主键最大id
保存在内存里,重启数据库或者是对表进行optimize
操作,都会导致最大id
丢失,重启后将max(id)+1
作为这个表当前的自增值。MySQL 8.0 版本
,自增主键最大id
记录在了redo log
中,重启后依靠redo log
恢复重启之前的值。
测试
1 | mysql> select * from employees_innodb; |
主键的选择
① 从性能和存储空间方面考量,自增主键往往是更好的选择:
- 存储空间少,自增主键一般用整型做主键,则二级索引叶子节点只要 4 个字节;而如果使用业务字段做主键,比如字符串类型的身份证号,每个二级索引的叶子节点得占用约 20 个字节。
- 性能好,插入数据时,自增主键能保证有序插入,避免了页分裂。
② 自增 id
是整型字段,推荐使用 unsigned int
类型来定义增长主键,可以存储 42 亿数据。
类型 | 大小 | 范围(有符号) | 范围(无符号) |
---|---|---|---|
int | 4 byte | (-2147483648, 2147483647) | (0, 4294967295) |
bigint | 8 byte | (-9223372036854775808, 9223372036854775807) | (0, 18446744073709551615) |
③ 自增 id
是增长的,但不一定连续,造成自增 id
不连续的情况可能有:
- 唯一键冲突
- 事务回滚
insert ... select
语句批量申请自增id