在普通的 insert 语句里面包含多个 value 值的情况下 , 即使 innodb_autoinc_lock_mode 设置为 1,也不会等语句执行完成才释放锁 。因为这类语句在申请自增 id 的时候,是可以精确计算出需要多少个 id 的,然后一次性申请 , 申请完成后锁就可以释放了 。从开始接触MySQL,我们就知道在设计主键时,要设置为自增主键,使用自增主键有以下几个优点:
- 效率高:使用自增主键可以避免频繁生成主键值的操作,节省了数据库的资源 , 提高了查询效率 。
- 索引优化:自增主键一般是整数类型,可以方便地使用B-tree索引来加速数据查询 。
- 数据唯一性:自增主键可以保证数据的唯一性,防止重复插入数据 。
- 方便性:使用自增主键可以方便地进行更新、删除和查询操作 , 不需要复杂的联合主键或其他索引操作 。
笔者先给出本文结论:自增主键可以保持主键递增顺序插入,避免页分裂,索引更为紧凑,但是自增主键并不能保证连续递增,即出现空洞 。
但是问题再次出现,为什么明明是自增主键,为什么不能保证连续递增?为什么会出现空洞?
在本文中,我们使用如下的数据库配置:
文章插图
1 自增值的存储在如上的空表 t 里面执行 insert into t values(null, 1, 1); 插入一行数据,再执行 show create table 命令,就可以看到如下图所示的结果:
文章插图
表定义里面出现了一个 AUTO_INCREMENT=2,表示下一次插入数据时 , 如果需要自动生成自增值,会生成 id=2 。
不同的引擎对于自增至的保存策略不同:
- MyISAM 引擎的自增值保存在数据文件中 。
- InnoDB 引擎的自增值,其实是保存在了内存里,并且到了 MySQL 8.0 版本后,才有了“自增值持久化”的能力,也就是才实现了“如果发生重启,表的自增值可以恢复为 MySQL 重启前的值”,具体情况是:
- 在 MySQL 5.7 及之前的版本,自增值保存在内存里,并没有持久化 。每次重启后,第一次打开表的时候,都会去找自增值的最大值 max(id),然后将 max(id)+1 作为这个表当前的自增值 。举例来说 , 如果一个表当前数据行里最大的id 是10,AUTO_INCREMENT=11 。这时候,我们删除 id=10 的行,AUTO_INCREMENT 还是 11 。但如果马上重启实例,重启后这个表的 AUTO_INCREMENT 就会变成 10 。也就是说,MySQL 重启可能会修改一个表的 AUTO_INCREMENT 的值 。
- 在 MySQL 8.0 版本,将自增值的变更记录在了 redo log 中,重启的时候依靠 redo log 恢复重启之前的值 。
- 如果插入数据时 id 字段指定为 0、null 或未指定值,那么就把这个表当前的 AUTO_INCREMENT 值填到自增字段;
- 如果插入数据时 id 字段指定了具体的值 , 就直接使用语句里指定的值 。
- 如果 X<Y,那么这个表的自增值不变;
- 如果 X≥Y,就需要把当前自增值修改为新的自增值 。
但是在一些场景下,使用的就不全是默认值 。比如 , 双 M 的主备结构里要求双写的时候,我们就可能会设置成 auto_increment_increment=2 , 让一个库的自增 id 都是奇数,另一个库的自增 id 都是偶数,避免两个库生成的主键发生冲突 。
推荐阅读
- Java中的序列化和反序列化它们的作用和用途是什么?
- 一文了解Redis的持久化
- Go 框架 Beego 真的有那么差劲吗
- 分布式场景下的事务机制
- MySQL数据库性能优化中常用的方法是什么?
- 一款开源的Linux可视化管理项目,运维效率翻倍
- 大模型微调非得依赖人类数据吗?DeepMind:用带反馈的自训练更好
- wlan和wifi之间的区别是什么呢?很多人都分不清,别再不知道了
- 准备好了吗?和Wi-Fi 7一起冲浪
- 想要网站排名快人一步?清楚了解网站的访问速度是关键!