上一篇:一个90后员工猝死的全过程

如果你用过或了解过MySQL,那你一定知道自增主键了。每个自增id都是定义了初始值,然后按照指定步长增长(默认步长是1)。虽然,自然数是没有上限的,但是我们在设计表结构的时候,通常都会指定字段长度,那么,这时候id就有上限了。既然有上限,就总有被用完的时候,如果id用完了,怎么办呢?今天就一起来学习下吧。

自增id

说到自增id,相信你的第一反应一定是在设计表结构的时候自定义一个自增id字段,那么就有一个问题啦,在插入数据时有可能唯一主键冲、sql事务回滚、批量插入的时候,批量申请自增值等原因导致自增id是不连续的。

表定义的自增值达到上线后的逻辑是:再申请下一个id的时候,获取的是同一个值(最大值)。大家可以插入sql设置id是最大值,再insert一条不主动设置id的语句就可以验证这一结论啦。这个时候如果再插入就是报主键冲突咯~

这里提醒一下:232-1(4294967295)不是一个特别大的数,对于一个频繁插入删除数据的表来说,是可能会被用完的。因此在建表的时候你需要考察你的表是否有可能达到这个上限,如果有可能,就应该创建成 8 个字节的 bigint unsigned。

InnoDB系统自增row_id

如果你创建的 InnoDB 表没有指定主键,那么 InnoDB 会给你创建一个不可见的,长度为 6 个字节的 row_id。InnoDB 维护了一个全局的 dict_sys.row_id 值,所有无主键的 InnoDB 表,每插入一行数据,都将当前的 dict_sys.row_id 值作为要插入数据的 row_id,然后把 dict_sys.row_id 的值加 1。

实际上,在代码实现时 row_id 是一个长度为8字节的无符号长整型 (bigint unsigned)。但是,InnoDB 在设计时,给 row_id 留的只是 6 个字节的长度,这样写到数据表中时只放了最后 6 个字节,所以 row_id 能写到数据表中的值,就有两个特征:

row_id 写入表中的值范围,是从 0 到 248-1;

当 dict_sys.row_id=2^48时,如果再有插入数据的行为要来申请 row_id,拿到以后再取最后 6 个字节的话就是 0。

虽然,2^48这个数字已经很大了,但是大家要知道 一个系统是可以跑很久的,那么还是可能达到上限的,这时候再申请就会覆盖原来的记录了。因此,尽量不要选择这种方式!

Xid

MySQL中redo log 和 binlog 相配合的时候,它们有一个共同的字段叫作 Xid。它在 MySQL 中是用来对应事务的。

MySQL 内部维护了一个全局变量 global_query_id,每次执行语句的时候将它赋值给 Query_id,然后给这个变量加 1。如果当前语句是这个事务执行的第一条语句,那么 MySQL 还会同时把 Query_id 赋值给这个事务的 Xid。而 global_query_id 是一个纯内存变量,重启之后就清零了。所以在同一个数据库实例中,不同事务的 Xid 也是有可能相同的。

Innodb trx_id

InnoDB 内部维护了一个 max_trx_id 全局变量,每次需要申请一个新的 trx_id 时,就获得 max_trx_id 的当前值,然后并将 max_trx_id 加 1。

InnoDB 数据可见性的核心思想是:每一行数据都记录了更新它的 trx_id,当一个事务读到一行数据的时候,判断这个数据是否可见的方法,就是通过事务的一致性视图与这行数据的 trx_id 做对比。但是这个过程有脏读存在,那么这个id就不会是原子性的,存在重复的可能性。

thread_id

其实,线程 id 才是 MySQL 中最常见的一种自增 id。平时我们在查各种现场的时候,show processlist 里面的第一列,就是 thread_id。

thread_id 的逻辑很好理解:系统保存了一个全局变量 thread_id_counter,每新建一个连接,就将 thread_id_counter 赋值给这个新连接的线程变量。

thread_id_counter 定义的大小是 4 个字节,因此达到 232-1 后,它就会重置为 0,然后继续增加。结果跟row_id一样,就会覆盖原有记录了。

上面介绍了几种MySQL自身的一些自增id,其实,实际运用中,我们也可能会选择外部的自增主键,然后持久化到数据库,以此来代替数据库自身的自增id。下面来说说吧。

Redis自增主键

其实外部自增主键的生成方式有很多,为什么我要介绍redis呢?因为我自己在实际应用中使用发现它的很多优点。

redis自身是原子性的,因此高并发也是线程安全的。假设主键字段长度20,我们以时间+自增数来构成主键,例如:8位日期+12自增数。那么,根据业务性质可以决定时间取年月日或者到毫秒级,那么在毫秒之间自增数的重复概率是极小极小的,基本的业务都能适用。

总结

上面介绍了好几种自增id,每种自增 id 有各自的应用场景,在达到上限后的表现也不同:

1、 表的自增 id 达到上限后,再申请时它的值就不会改变,进而导致继续插入数据时报主键冲突的错误

2、 row_id 达到上限后,则会归 0 再重新递增,如果出现相同的 row_id,后写的数据会覆盖之前的数据

3、 Xid 只需要不在同一个 binlog 文件中出现重复值即可。虽然理论上会出现重复值,但是概率极小,可以忽略不计

4、 InnoDB 的 max_trx_id 递增值每次 MySQL 重启都会被保存起来,所以我们文章中提到的脏读的例子就是一个必现的 bug,好在留给我们的时间还很充裕

5、 thread_id 是我们使用中最常见的,而且也是处理得最好的一个自增 id 逻辑了

6、 redis外部自增,毫秒级别,理论上会出现重复值,但是概率极小,可以忽略不计

7、 其实,每种自增id都有各自的适用场景,大家在平时使用中可以根据具体场景再选择。但是要未雨绸缪,因为系统的运行时间和数据的存储,这些都是要考虑在内的,综合考虑,选择一个在系统运行期间一定不会出现重复即刻。你学会了吗?

最后,关注公众号互联网架构师,在后台回复:2T,可以获取我整理和创作的 Java 系列教程非常齐全。

推荐阅读

1、2019 年 9 月全国程序员工资统计,你是什么水平?

2、如何才能成为优秀的架构师?

3、从零开始搭建创业公司后台技术栈

4、程序员一般可以从什么平台接私活?

5、37岁程序员被裁,120天没找到工作,无奈去小公司,结果懵了...

6、滴滴业务中台构建实践,首次曝光

7、不认命,从10年流水线工人,到谷歌上班的程序媛,一位湖南妹子的励志故事

8、15张图看懂瞎忙和高效的区别!

面试官问:MySQL的自增 ID 用完了,怎么办?相关推荐

  1. 面试官问:如果MySQL的自增 ID 用完了,怎么办?

    欢迎关注方志朋的博客,回复"666"获面试宝典 如果你用过或了解过MySQL,那你一定知道自增主键了.每个自增id都是定义了初始值,然后按照指定步长增长(默认步长是1).虽然,自然 ...

  2. 面试官问:MySQL 的自增 ID 用完了,怎么办?

    点击上方"方志朋",选择"设为星标" 回复"666"获取新整理的面试文章 来源:程序猿面试指南 既然这块知识点不清楚,那回头就自己动手实践下 ...

  3. 怎么定义int_面试官问:MySQL的自增ID用完了,怎么办?!

    既然这块知识点不清楚,那回头就自己动手实践下. 首先,创建一个最简单的表,只包含一个自增id,并插入一条数据: ------- create table t0(id int unsigned auto ...

  4. 如果MySQL的自增 ID 用完了,怎么办?

    作者 | 方志朋 来源 | https://mp.weixin.qq.com/s/Yqo5PaTtQcQTn4p8BE6SGg 如果你用过或了解过MySQL,那你一定知道自增主键了.每个自增id都是定 ...

  5. 面试官问:数据库 delete 表数据,磁盘空间还是被一直占用,为什么?

    以下文章来源方志朋的博客,回复"666"获面试宝典 最近有个上位机获取下位机上报数据的项目,由于上报频率比较频繁且数据量大,导致数据增长过快,磁盘占用多. 为了节约成本,定期进行数 ...

  6. .jar中没有主清单属性_面试官问:为什么SpringBoot的 jar 可以直接运行?

    点击上方蓝色字体,选择"设为星标" 优质文章,及时送达 来源 | https://urlify.cn/uQvIna SpringBoot提供了一个插件spring-boot-mav ...

  7. 【176期】面试官:MYSQL 表数据 delete 删除后,为何还占用存储空间?

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每天 08:35 更新文章,每天进步一点点... ...

  8. 【169期】面试官问:说说为什么要限流,有哪些解决方案?

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜留言必回,有问必答! 每天 08:35 更新文章,每天进步一点点... ...

  9. 线上 MySQL 的自增 id 用尽怎么办?被面试官干趴下了!

    点击下方"Java编程鸭"关注并标星 更多精彩 第一时间直达 MySQL的自增id都定义了初始值,然后不断加步长.虽然自然数没有上限,但定义了表示这个数的字节长度,计算机存储就有上 ...

  10. 【283期】面试官问:高并发场景下,如何保证全局唯一分布式 ID 生成?

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每一天进步一点点,是成功的开始... 前言 系统 ...

最新文章

  1. CentOS安装jdk(无需配置环境变量)
  2. SAP CAM - Cloud Access Manager
  3. C#中OpenFileDialog获取文件名和文件路径的常用方法.
  4. 赢在CSDN —— 我们一起向前
  5. 12个有趣的C语言面试题
  6. 读懂这篇文章,你的阿里技术面就可以过关了 | Apache RocketMQ 101
  7. 【转】Elasticsearch5.0 安装问题集锦
  8. 我们一起学习WCF 第五篇数据协定和消息协定
  9. windows系统迁移,C盘搬家
  10. 双眼融合训练一个月_最好的双眼视功能训练方法
  11. 彻底搞懂CNN中的卷积和反卷积
  12. 基于位置指纹匹配的定位算法
  13. 已解决 You can enable repos with yum-config-manager --enable <repo>
  14. 论文-Deep Neural Networks are Easily Fooled: High Confidence Predictions for Unrecognizable Images
  15. 在微控制器平台等小型物联网设备上运行 JavaScript
  16. drippingblues-靶机渗透
  17. FFMPEG 将IPCamera的RTSP视频流传送至RED5服务器 小白教程
  18. Excel学习——制作周报
  19. SpringBoot员工管理系统(整合Mybatis+mysql)
  20. 【自动控制原理】拉氏变换

热门文章

  1. Linux内核提供了三种不同形式的中断底半部实现机制:软中断、tasklet和工作队列。...
  2. 一个正在被API驱动的互联网时代
  3. 详解YUV系列(二)--YUV422
  4. sed学习笔记(1) - 入门知识
  5. 【linux】——FTP出现500 OOPS: cannot change directory的解决方法
  6. 【深入篇】Android常用布局方式简介
  7. JavaScript 省市县数据
  8. Spring--超简单利用quartz实现定时作业
  9. 一道曾经微软的面试题
  10. QuickTime Player 如何开启倍速播放?