背景
问题:
有一张表,里面有 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