作者:贲绍华

爱可生研发中心工程师,负责项目的需求与维护工作。其他身份:柯基铲屎官。

本文来源:原创投稿

*爱可生开源社区出品,原创内容未经授权不得随意使用,转载请联系小编并注明来源。


当我们使用 MySQL 进行数据存储时,一般会为一张表设置一个自增主键,当有数据行插入时,该主键字段则会根据步长与偏移量增长(默认每次+1)。

下文以 Innodb 引擎为主进行介绍,使用自增主键的好处有很多,如:索引空间占比小、范围查询与排序都友好、避免像 UUID 这样随机字符串带来的页分裂问题等…

一、自增ID是如何分配的?

1.1 计数器的初始化

当我们对该表设置了自增主键之后,则会在该表上产生一个计数器,用于为自增列分配 ID 。

自增的值并不是保存在表结构信息内的,对于不同的版本它们有如下的区别:

1.1.1 MySQL 8.0 版本之前(重启后可能会产生变化):

计数器的值存储在内存中的,重启后丢弃,下一次将读取最大的一个自增ID往后继续发号。

https://dev.mysql.com/doc/refman/5.7/en/innodb-auto-increment-handling.html#innodb-auto-increment-initialization

1.1.2 MySQL 8.0版本(重启后保持不变):

计数器的值将会持久化到磁盘。在每次发号时都将写入 Redolog ,并在每个 Checkpoint 都进行保存,重启时候使用 Redolog 恢复重启之前的值。

https://dev.mysql.com/doc/refman/8.0/en/innodb-auto-increment-handling.html#innodb-auto-increment-initialization

1.2 数据的插入形式

1.2.1 Simple Inserts(简单插入):

可以预先确定插入行数的语句(像简单 insert 的语句包含多个 value 这种情况也是属于简单插入,因为在进行插入时就已经可以确定行数了)

1.2.2 Bulk Inserts(大量插入):

预先不知道要插入的行数的语句(包括 INSERT … SELECT, REPLACE … SELECT 和 LOAD DATA 语句,但不包括 plain INSERT )

1.3 AUTO-INC 表级锁

如果一个事务正在向表中插入值,则会产生表级的共享锁,以便当前事务插入的行接收连续的主键值。

1.3.1 加锁策略:

当处于[ 传统模式 ]与[ 连续模式 ]时,每次访问计数器时都会加上一个名为 AUTO-INC 的表级锁

1.3.2 释放策略:

传统模式:锁只持有到该语句执行结束,注意是语句结束,不是事务结束

连续模式:批量插入时锁持有到该语句执行结束,简单插入时锁持有到申请完自增ID后即释放,不直到语句完成

1.4 计数器的三种模式(innodb_autoinc_lock_mode)

通过调整 innodb_autoinc_lock_mode 配置项,可以定义 AUTO-INC 锁的模式,不同的模式对应的策略与锁的粒度也将不同。

当使用基于 Binlog 的复制场景时,对于 statement(SBR)同步模式下只有[ 传统模式 ]与[ 连续模式 ]能保证语句的正确性。

基于 row(RBR)行复制的情况下任何配置模式都可以。

1.4.1 传统模式 [ innodb_autoinc_lock_mode = 0 ]

执行语句时加 AUTO-INC 表级锁,执行完毕后释放

1.4.2 连续模式 [ innodb_autoinc_lock_mode = 1 ] [ 8.0版本之前为默认 ]

针对 Bulk Inserts 时才会采用 AUTO-INC 锁,而针对 Simple Inserts 时,则采用了一种新的轻量级的互斥锁来分配 auto_increment 列的值。

该模式下可以保证同一条 insert 语句中新插入的自增 ID 都是连续的,但如果前一个事务 rollback 丢弃了一部分 ID 的话也会存在后续 ID 出现间隔的情况。

1.4.3 混合模式 [ innodb_autoinc_lock_mode = 2 ] [ 8.0版本为默认 ]

来一个分配一个,不会产生 AUTO-INC 表级锁 ,仅仅会锁住分配 ID 的过程。

由于锁的粒度减少,多条语句在插入时进行锁竞争,自增长的值可能不是连续的。

且当 Binlog 模式为 statement(SBR)时自增 ID 不能保证数据的正确性

1.5 自增 ID 一定就是连续吗?

不一定,业务也不应该过分依赖 MySQL 自增 ID 的连续性,在以下三种情况下,并不能保证自增 ID 的连续性:

1.5.1 插入时的其他唯一索引冲突

假设已存在数据{1,张三},且张三所属的字段设置了唯一主键

此时再次插入{null,张三}时候,主键冲突插入失败,但表的计数器已由2变成了3

当下次插入{null,李四}的时候最终入库的会变成{3,李四}

1.5.2 事务回滚

在一个事务里进行数据的插入,但最后并没提交,而是执行了 Rollback 。那么计数器已递增的 ID 是不会返还的,而是被直接丢弃。

1.5.3 发生 Bulk Inserts(大量插入)时

发生大量插入时可能会出现自增 ID 并不是连续的情况

二、自增 ID 用完了该怎么办?

当我们为表设置了自增主键后,自增 ID 的范围则与主键的数据类型长度相关。

如果没有一张表里没有设置任何主键,则会自动生成一个隐性的6字节的 row_id 作为主键,它的取值范围为 0 到 2^48-1。

row_id 是由一个全局的 dict_sys.row_id 参数进行维护的,所有没有主键的表都会用上它(并不是每一个表单独占一份 row_id list )

那么针对这两种主键,则会有以下两种情况发生:

2.1 当自增主键用完了:

当自增 ID 到达上限后,受到主键数据类型的影响,计数器发放的下一个 ID 也是当前这个 Max ID ,当执行语句时则会提示主键冲突。

1062 - Duplicate entry ‘4294967295’ for key ‘PRIMARY’

建议根据业务合理规划,在进行表设计时就选择适合的数据类型。

当然也可以直接选择 Bigint 类型,它的取值范围是无符号情况下:0到 2^64–1(18446744073709551615)

这里并不是指 bigint 类型一定不会用完,毕竟一个有范围的持续增长的值一定会有溢出的时候,只是说一般场景下它都是足够使用的。

一秒增加的记录数 大约多少年后才会用完
1/秒 584942417355 年
1万/秒 58494241 年
100万/秒 584942 年
1亿/秒 5849 年

2.2 当 row_id 用完了

当 row_id 使用完后则又会从 0 开始发放,此时新插入的数据将覆盖回 row_id=0 的数据行。

由于它并不产生错误,还会造成数据的覆盖写。所以我们平时还是尽量给表都设置一个合理的主键才是。

三、自增 ID 的暴露导致被爬虫恶意遍历该怎么办?

在实际业务场景中,ID 常常需要返回给客户端用来进行相关业务操作。

假如我们有个 userinfo?uid=? 的 API 接口,而用户 ID 是自增的,这时会发生什么?

该接口通过简单的尝试就可以暴露出真实的业务用户总数,可以很方便的使用爬虫从1开始递增获取数据信息。

那么有的同学说,我既想使用自增 ID 带来的好处,也不想承受这种比较常见的问题,那该怎么办呢?

3.1 自增 ID 输入输出前进行转义

在输出或者获取前对指定字段进行可逆的转义操作

优点:实现起来比较简单,无论单体业务或者分布式应用都无需考虑对数据源的解析,只需在客户端实现自己的转义与解析方法即可;

缺点:业务入侵较大,且需要前后端各个合作方确认统一的标准;如果转义方法有调整,变更影响面也会很大;字符串长度会随ID长度而变化,使用空位填充也会特别明显;

3.2 Snowflake 花算法:

优点:由于采用了时间戳进行 ID 生成,该 ID 是有序的,对范围查询与排序都比较友好;

缺点:需要保证发号节点的高可用性;另外由于生成时依赖时间戳,需要考虑时钟回拨与时钟同步的问题;

3.3 使用 HashMap 进行映射:

维护一份 ID 与 hash 的映射字典,它可以存在于客户端本身,也可以依赖其他如 Redis 、ETCD 之类的组件

优点:hash 长度不会随着 ID 长度或值的变化而变化;可以根据已有的 hash code 来造布隆过滤器;

缺点:业务入侵较大,查询时同样需要先根据 hash key 找到对应的 ID 值;需要考虑选择合适的 hash 算法以及解决 hash 冲突或扩容的问题。

技术分享 | 关于 MySQL 自增 ID 的事儿相关推荐

  1. mysql自动增长id 溢出_MySQL表自增id溢出的故障复盘怎么解决 MySQL表自增id溢出的故障复盘解决方法...

    MySQL表自增id溢出的故障复盘如何解决?本篇文章小编给大家分享一下MySQL表自增id溢出的故障复盘解决方法,小编觉得挺不错的,现在分享给大家供大家参考,有需要的小伙伴们可以来看看. 问题:MyS ...

  2. mysql自增id原理_《MySQL自增ID》告诉你不为人知的“秘密”......

    原标题:<MySQL自增ID>告诉你不为人知的"秘密"...... 作者:Sunshine Koo 1.概述 " MySQL数据库是最常使用的数据库之一,我们 ...

  3. 45 MySQL自增id

    45 MySQL自增id 表定义自增id 说到自增id,前面提到mysql的自增id不连续,当表定义的自增值达到上限后的逻辑是:再申请下一个id时,得到的值保持不变 create table t(id ...

  4. 修改 MySQL 自增ID的起始值

    修改 MySQL 自增ID的起始值 alter table users AUTO_INCREMENT=10000; posted on 2019-04-15 17:45 流易 阅读(...) 评论(. ...

  5. mysql自增id用完了_MySQL表自增id用完了该怎么办?

    我们知道MySQL表可以定义一个自增长的id,如果我们的表没有指定主键字段,那MySQL会给我们的表创建一个不可见的,长度为6个自己的row_id,然后不停地往上加步长,虽然生活中自然数是没有上限的, ...

  6. 有关于mysql自增型需要返回id_关于mysql自增id,你需要知道的

    关于mysql自增id,你需要知道的,主键,重启,索引,类型,字段 关于mysql自增id,你需要知道的 易采站长站,站长之家为您整理了关于mysql自增id,你需要知道的的相关内容. 导读:在使用M ...

  7. mysql自增id用完了_MySQL 自增 ID 用完了怎么办?

    MySQL 自增 ID 用完了怎么办? 在MySQL中有很多类型的自增ID,每个自增ID都设置了初始值,然后按照一定的步长增加,只要定义了字节长度,那么就会有上限,如果达到上限再次添加,则会报主键冲突 ...

  8. mysql自增id跳跃增长不连续,auto_increment_increment的问题

    问题: 发现测试服务器上mysql自增id不连续,以8的倍数跳跃,像这样8,16,24,32... 查找网上资料大多说的都是起始id不是从1开始,或者删除后再插入id不连续... 我的情况是,id不连 ...

  9. mysql id问题_关于MySQL自增ID的一些小问题总结

    下面这几个小问题都是基于 InnoDB 存储引擎的. 1. ID最大的记录删除后,新插入的记录ID是什么 例如当前表中有ID为1,2,3三条记录,把3删除,新插入记录的ID从哪儿开始? 答案: 从4开 ...

最新文章

  1. 让你的网站提速:图片优化网站推荐
  2. Could not open a connection to your authentication agent
  3. 斯坦福CS231 BP后向传播
  4. 重启网卡服务_Linux下查看不到物理网卡配置
  5. 在阿里干了5年招聘,这10条建议我必须分享给你!
  6. java线上排查利器arthas
  7. Linux基础知识(1)
  8. java学习(57):内部类
  9. scandall pro找不到扫描仪_吉林机箱风扇灯条找哪家
  10. AWK 之 RS、ORS与FS、OFS
  11. 80多个Ajax解决方案
  12. Strategy模式的一点思考
  13. #100天计划# 2013年10月8日
  14. json-server安装报错问题
  15. 使用DX查看系统配置
  16. Vm虚拟机安装Linux系统教程
  17. matlab c1083,致命錯誤C1083:不能打開包含文件:'mexutils。沒有這樣的文件或目錄。...
  18. Halium 9 尝鲜 -- 在小米平板4上的移植 (四)
  19. 一起赚美元⑥ | 创立Discourse开源论坛软件每月赚取12万美元的故事
  20. code block怎样导入整个文件夹_PR怎样大批量添加字幕? 协同AE。

热门文章

  1. iMessage与微信
  2. 出现git@github.com: Permission denied (publickey)的解决方法
  3. c语言 异或_C语言知识点:运算符的优先级和结合性
  4. python 遍历嵌套字典
  5. Outlook回复邮件自动加上附件
  6. 进阶DV用户的选择 精品点评导购推荐
  7. java json转换xml_在Java中将JSON转换为XML
  8. 公司自建电商系统对接Ariba PunchOut ----踩坑之路
  9. 《数字图像处理》DFT(离散傅里叶变换)及HF(同态滤波)的实现
  10. 字符串匹配算法BF,BM,KMP