面试官:MYSQL自增id超过int最大值怎么办?

故事背景

今天运维那边反馈有一个设备在后台查不到,我第一时间怀疑可能是数据出了问题,导致服务报错了没有入库。我拿着日志去本地请求接口,发现程序是没有报错的,我们的逻辑是先把唯一id放到redis里面,如果redis没有值就insert,有就update,做了一层缓存,估计是这样的话批量插入和更新数据库会快一点。然后我看redis是有值的,以为是redis和数据库数据不一致问题,我就把redis的key删了,重新再跑一下,结果打印了insert语句,但是没有插入到数据,看来事情并没有那么简单- -

问题分析

因为数据表很大,有5E+数据,我第一反应是mysql表数据量可能爆了,但是查了下好像没有太大限制

再认真看了下表的自增id,这个数字让人有点熟悉的:2147483647 这个不就是int的最大值吗。意思是因为自增id超过了int,所以插入失败了,id设的就是int类型,还有个小彩蛋,目前数据库设的int长度是50,但是根本没什么鸟用。

知道了问题在哪,但是这个问题处理起来很麻烦,因为数据量太大了,先请教一下deepseek吧。

大厂机会

想选一个大厂作为跳板,作为自己镀金机会的,尤其是看【上海】【深圳】等[→机会的]朋友,前端-测试-后端!待遇薪酬还不错,尽管来!

方案处理

deepseek给我提供了三个方案,第一个是最简单粗暴的改BIGINT,不用迁移数据,但是会全程锁表。第二个分布式ID需要重新设计表,需要把数据迁移到新表,而且还要redis等支撑。第三个分库分表就更麻烦了,分库分表需要引入框架,不按照分片查询还需要引入ES,引入了ES还需要引入同步mysql和ES的中间件logstash等。具体可看我之前的文章

juejin.cn/post/744401…

但是改bigint估计锁表太久,我先看看有没有其他办法先紧急处理下数据。但是按理说int最大值是21E+,数据表数据才5E+,按理说是用不完的。结果我看到自增的id值居然是不连续的

按理说自增id应该是一个接着一个,不会有空隙的,后面查了一下由于数据库自增id有个高性能策略,设置了id就不一定连续。

后面又查了下有没有一键把数据表id重排的方法,结果也是没有的。最后我是写了一个存储过程先把最后100万的id清理出来,可以先顶个几天,后面再想办法处理。

BEGIN
  DECLARE start_id INT DEFAULT 1;
  DECLARE end_id INT DEFAULT 100000;
  DECLARE current_batch INT DEFAULT 0;
  
  WHILE start_id <= end_id DO
    -- 更新临时表中的ID
    UPDATE table 
    SET id = start_id + 1
    WHERE id = (select original_id from (
      SELECT id AS original_id 
      FROM table 
      ORDER BY id DESC 
      LIMIT 1) as test);
    
    SET start_id = start_id + 1;
  END WHILE;
END

最后重新设置自增值,如果自增值已经存在,则会跳到max(id)+1

-- 重置自增值
ALTER TABLE your_table AUTO_INCREMENT = max(id)+1;

清理了大概500万的id段出来,然后我怀疑id间隔这么大是因为并发太高导致的。一开始程序是单线程,消费到500条就批量入库,但是后面发现单线程消费比较慢,数据量太多消费有点延迟。后面改成java批量消费,配置了30个消费者。接着我尝试了一下减少消费者数量,设置成15个,id的间隔真的变小了。

设置BIGINT

节后回来发现id还剩200万,讨论到最后还是把id的数据类型从int改成bigint

ALTER TABLE xxx MODIFY id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT

UNSIGNED 无符号位,不算负数,可以增加一倍数据,NOT NULL 非空 AUTO_INCREMENT自增

在测试环境有一亿数据,修改id的类型大概用了一个小时,现网我估计也是用6-7个小时也差不多了。结果改了一晚上都还没改好,然后我找了一个可以查询sql进度的语句......

SELECT EVENT_NAME, WORK_COMPLETED, WORK_ESTIMATED, ROUND(WORK_COMPLETED/WORK_ESTIMATED*100, 2) AS "Progress (%)" FROM performance_schema.events_stages_current;

不查不知道,一查吓一跳,跑了十几个小时居然还不到50%,而且还越跑越慢。对比了一下测试环境和现网环境的buffer_pool等数据也是设置正常。

估计是索引树变大插入的数据要花多不少时间,还有一个就是现网数据库还有其他线程会抢占CPU导致速度缓慢。

统计了一下后面的数据大概是1个小时完成1.5%左右

最后我是周一晚上执行的,周四早上上班的时候才跑完,用了2天多一点的时间~

总结

有几个原因吧:

1、数据量确实很大,有5E多数据,然后并发也很高。其实当初他们设计的时候也预料过这个问题,所以设了个int长度50,但是这个长度没起作用- -所以设计数据库的时候一定要做好,不然几亿数据改个字段类型要2天

2、数据库的自增id策略选了高性能策略,导致并发高的时候id间隔很大。30个消费者异步处理,10条数据大概用了100个id的间隔,消耗太快了。所以这里存在一个时间和空间的取舍,使用多线程还是挺危险的操作,要谨慎一点

还有一个小插曲,因为系统两天没消费数据,kafka的数据堆积了很多,然后我把消费者数量从30个改成50个,跑了两天,kafka还是有1天的延迟,看来麻木添加消费者数量已经没啥提升的作用了,想起八股文说多线程弄太多反而增加上下文切换的时间浪费,跟这个同理

最后我弄成sql批量消费,消费速度马上提上去了。程序的消费策略:

单线程批量500个开始消费 ——> 30个线程单个消费 ——> 30个线程批量50个开始消费

所以说多线程异步+批量操作的策略还是很重要的!不过多线程一定要注意异步问题~

——转载自:玛奇玛丶

#软件开发投递记录#
全部评论
自增每次都会产生锁的开销,虽然单行不大批量游艇大问题的,直接喷面试官提交怎么问这么low的题
点赞 回复 分享
发布于 12-14 17:37 江西

相关推荐

不愿透露姓名的神秘牛友
12-12 23:16
华勤技术 信息安全工程师 16k x 15薪 硕士海归
点赞 评论 收藏
分享
后端实习中的&nbsp;“好需求”,核心定义是能支撑面试深度讨论、可向外延伸多维度知识点的需求——&nbsp;本质是能让你在面试官拷打时,有足够空间展现技术积累、解决问题的能力,而非仅完成简单&nbsp;CRUD。结合面试反推逻辑,具体可分为三类,且都具备&nbsp;“可延伸、有讨论点”&nbsp;的共性。本质上是这个需求要支撑你能给面试官吹牛逼。典型的垃圾需求:或许有的同学可能还不理解什么叫做可以吹牛逼的需求,我举一个最简单的反例,很多同学写苍穹外卖的时候,总爱把一个需求写到简历上:&nbsp;&nbsp;基于OSS处理用户上传图片,获取OSS返回URL,实现用户远程上传图片。这就是个最典型的垃圾需求。因为你发现论代码链路,他没什么可讲的。论各种新潮技术,他也...
反装笔大队长:分情况吧。需求分业务需求和技术需求,技术需求你说的是对的。像CRM、OA、NC等等,这些业务系统很多时候对技术要求并不高的,不可否认的是 这些需求还是很不错的。 NC系统的进销存。实际上只是对仓库、库位、库存量、入库出库单价、数据报表等数据的统计与计算。CRM的市场活动、人面画像分析与统计、客户信息管理等,这些无非都是一些增删改查。对于业务需求面试官通常都是问你对业务的理解与过往对该业务的处理方案,并不会死磕技术。技术肯定是多多益善,但在业务开发中 正在有意义的是你的经历。
投递字节跳动等公司7个岗位
点赞 评论 收藏
分享
评论
1
3
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务