mysql 主键自增语句_MySQL 自增主键[通俗易懂]

mysql 主键自增语句_MySQL 自增主键[通俗易懂]以下仅考虑InnoDB存储引擎。自增主键有两个性质需要考虑:单调性每次插入一条数据,其ID都是比上一条插入的数据的ID大,就算上一条数据被删除。连续性插入成功时,其数据的ID和前一次插入成功时数据的ID相邻。自增主键的单调性为何会有单调性的问题?这主要跟自增主键最大值的获取方式,以及存放位置有关系。如果最大值是通过计算获取的,并且在某些情况下需要重新获取时,会因为最新的数据被删…

大家好,又见面了,我是你们的朋友全栈君。

以下仅考虑 InnoDB 存储引擎。

自增主键有两个性质需要考虑:

单调性

每次插入一条数据,其 ID 都是比上一条插入的数据的 ID 大,就算上一条数据被删除。

连续性

插入成功时,其数据的 ID 和前一次插入成功时数据的 ID 相邻。

自增主键的单调性

为何会有单调性的问题?

这主要跟自增主键最大值的获取方式,以及存放位置有关系。

如果最大值是通过计算获取的,并且在某些情况下需要重新获取时,会因为最新的数据被删除而减小。

自增主键最大值怎么取的?存放到哪里?

MySQL 5.7 及之前的版本,自增主键最大值会在启动(重启)后从数据库中取出放到内存:

SELECT MAX(ai_col) FROM table_name FOR UPDATE;

这样获取是通过计算的,并且由于存放在内存而容易丢失。

如果删除最新一条数据(假设 ID 为 10),因故障或者其他必要重启后再插入一条数据时会使用之前的 ID (即 ID 为 10)。

问题在于如果有其他表依赖了该 ID,则其他表的数据关联到的数据就符合要求了。除非设置了外键。

比如我要向最大一个 ID 的账号充了 100 万。但是在充值之前,该账号被删除,然后服务器故障重启,重启后有人新注册了一个账号。结果我的 100 万充到了他的新账号上。注册新账号的人以为是新手福利,笑嘻嘻。

如何解决单调性的问题?

从 MySQL 8.0 开始,自增主键最大值会在每次修改后写入到 redo log,并且在每个检查点写入引擎私有的系统表。

如果是正常重启,则读取系统表里的值。

如果是故障重启,则先读取系统表里的值放到内存。接着扫描 redo log 里存储的值。如果扫描到的值大于内存的值,则将该值覆盖到内存。

但由于数据库可能在 redo log 刷入磁盘前就故障了,所以可能会用到之前申请的 ID。

注:如果 redo log 都没刷入,就更不用说将数据插入数据表了。

自增主键插入时的连续性

这里不考虑由于删除导致的连续性问题

为何会有连续性问题?

这主要是跟插入事务回滚有关系。

对于两个插入事务,事务 A 先执行插入语句,之后事务 B 执行插入语句。在这之后,事务 A 回滚,导致 A 执行插入语句时占用的 ID 被抛弃。

之所以事务 A 没提交的情况下,事务 B 就能执行插入语句,跟 InnoDB 的自增长锁(AUTO-INC Locking)相关。该锁是一种特殊的表锁(table-level lock),但会在插入语句执行后立即释放,不会等到事务结束。

如何解决连续性问题?

使用最高隔离级别 SERIALIZABLE (串行)。

由于性能上的考虑,通常不这样做。

多事务批量插入的连续性

事务 A 和事务 B 都在执行 不确定数量 的批量插入(INSERT … SELECT):

保证事务 A 的数据的 ID 连续: innodb_autoinc_lock_mode = 0 (AUTO-INC Locking)

必须等待语句执行结束才释放锁。

保证事务 A 的数据的 ID 连续: innodb_autoinc_lock_mode = 1 (AUTO-INC Locking)

和上面的区别在于,当执行 确定数量 的批量插入时,使用轻量级互斥量(mutex)而不是特殊表锁(AUTO-INC Locking),从而提前向内存的计数器申请相应数量的 ID。之后立即释放,不用等语句执行结束。

会因为回滚而使得全局 ID 不连续。

不保证事务 A 的数据的 ID 连续: innodb_autoinc_lock_mode = 2 (mutex)

三种插入定义:

简单插入

能够提前知道插入的行数

批量插入

不能提前知道插入的行数

混合插入

批量插入中的一部分的 ID 是指定的(非 0 且非 NULL),另一部分未指定,使用数据库生成的自增 ID。

其他

如果主动指定 ID 为 0 或者 NULL 插入,则会使用数据库生成的自增 ID。

参考文档

为什么 MySQL 的自增主键不单调也不连续

https://database.51cto.com/art/202004/614923.htm

《MySQL技术内幕——InnoDB存储引擎》 第 6 章:锁

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/147511.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • stm32看门狗定时器记录「建议收藏」

    stm32看门狗定时器记录「建议收藏」文章目录1、看门狗介绍2、独立看门狗3、窗口看门狗1、看门狗介绍STM32有两个看门狗,一个是独立看门狗另外一个是窗口看门狗。独立看门狗简单来说就是一个12位的递减计数器,当计数器的值从某个值一直减到0的时候,系统就会产生一个复位信号,即IWDG_RESET。如果在计数没减到0之前,刷新了计数器的值的话,那么就不会产生复位信号,这个动作就是我们经常说的喂狗。看门狗功能由VDD电压域供电,在停止模式和待机模式下仍能工作。因此我们就可以在程序死机的时候使用这个独立看门狗来复位程序,

    2022年6月6日
    24
  • android变化HOLO对话风格

    android变化HOLO对话风格

    2022年1月9日
    40
  • matlab如何循环_左手转笔教程无限循环

    matlab如何循环_左手转笔教程无限循环循环输出变量代码fori=1:4iend其中1:4代表一个行向量1234,在matlab中,行向量的另外一个表示方法是[1234],即fori=1:4等价于fori=[1234],编写程序时牢记一个点:对于某次固定的迭代,i会从这个向量中取一个值,该值可以参与循环中的计算。运行输出i=1i=2i=3i=

    2022年10月6日
    4
  • 群晖Virtual Machine Manager虚拟机安装OpenWrt软路由作为旁路由的详细步骤

    群晖Virtual Machine Manager虚拟机安装OpenWrt软路由作为旁路由的详细步骤0.前言:本来一直都是在Windows10的Hyper-V中虚拟软路由的,直到有一天突发奇想,手贱在windows10宿主机中安装了个安卓模拟器,由于众所周知的原因,安卓模拟器是不能同时与Hyper-V虚拟机共存的,虽然我在安装后运行安卓模拟器的时候没有去点击那个关闭Hyper-V的提示按钮,并且迅速点击了退出按钮,但是悲剧还是发生了,我的Windows10宿主机在重启后自动关闭了Hyper-V功能,导致我在其中安装的openwrt旁路由、centos测试环境都熄火了!然后就是赶紧在【程序】中添加【Hy

    2022年6月1日
    139
  • oracle函数查询结果遍历输出_initcap函数

    oracle函数查询结果遍历输出_initcap函数1、Oracle程序包查询SELECTDISTINCTNAMEFROMDBA_SOURCEWHEREtype=’PACKAGE’ORDERBYNAME;2、Oracle程序包中函数、存储过程源码查询SELECT*FROMDBA_SOURCEWHERETYPE=’PACKAGEBODY’;…

    2025年9月17日
    4
  • 百度为何开源paddleocr_智能边缘计算

    百度为何开源paddleocr_智能边缘计算PaddleServing作为飞桨(PaddlePaddle)开源的服务化部署框架,提供了C++Serving和PythonPipeline两套框架,旨在帮助深度学习开发者…

    2022年9月19日
    4

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号