首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >线上服务突然很慢,竟是有人在更新百万级数据

线上服务突然很慢,竟是有人在更新百万级数据

原创
作者头像
一只牛博
发布2025-09-13 17:10:28
发布2025-09-13 17:10:28
1250
举报

线上突发故障

那天下午,正在愉快地摸鱼,突然工作群里炸了:

张三:卧槽,线上系统怎么这么慢?

李四:你网络有问题吧,我这边还好啊

张三:我特意换了个网络,还是慢得要死

王五:奇怪,其他网站都正常,就我们系统慢,服务器挂了?

赵六:等等吧,可能过一会就好了

结果这一等不要紧,系统越来越慢,有的人直接进不去了,有的人页面加载超时。

王麻子:我本地开发环境一点事没有,飞快

刘能子:我本地连线上后端,卡得要死(前端同学,直接用的线上接口)

我一听这个对比就知道问题所在了。本地环境用的测试数据库,线上用的生产数据库。既然本地快、线上慢,那问题八成出在生产数据库上。

开始排查

赶紧登录服务器,进入MySQL看看到底什么情况。

先看看MySQL在忙什么

代码语言:sql
复制
SHOW PROCESSLIST;

这个命令能看到当前MySQL所有正在执行的操作,一跑出来我就傻眼了。

有一条update语句已经跑了300多秒了!而且还在updating状态,下面排了一堆等待的查询。

看到这我就明白了,肯定是有人在跑大批量的update操作,把整个数据库都堵住了。

继续深挖

群里问了一圈,果然有同事承认了:她刚才执行了一个update语句,而且更新的数据量是几百万条

我当时就懵了,几百万条数据,这是要干啥?

另一个同事着急问:这个可以kill掉吗?然后就直接kill了。结果kill完之后,那条SQL的状态变成了killed,但是还在那里,时间还在增长。

我一看就知道不妙了:事务回滚了

更新几百万条数据已经够恐怖了,现在还要回滚几百万条,这简直是雪上加霜啊。

看看事务到底在干什么

光知道有update在跑还不够,我得看看具体的事务状态:

代码语言:sql
复制
SELECT * FROM information_schema.innodb_trx;
WechatIMG14992
WechatIMG14992

这个结果让我倒吸一口凉气!

从图片可以看出,现在有好几个事务:

  • 第一行显示状态是ROLLING BACK,就是在回滚,权重是100多万
  • 第二行也是ROLLING BACK,权重80万
  • 第三行更恐怖,权重386万!

trx_weight这个数字代表什么意思? 简单来说就是这个事务影响了多少数据。386万的权重,意味着这个事务修改或锁定了386万行数据!

现在我彻底明白了:有人执行了巨大的update操作,然后被kill掉了,现在MySQL正在疯狂回滚这些修改。而回滚的过程中,这些数据依然是被锁定的,所以其他查询统统被阻塞了。

这就像是高速公路上翻了几辆大货车,后面所有车都得堵着等清理完才能通行。

怎么办?

这时候就面临选择了,要么暴力解决,要么等着。

方案一:直接重启MySQL

ebfe2ce7f2cfa9b68be361b48a7a74d7
ebfe2ce7f2cfa9b68be361b48a7a74d7

最简单粗暴的办法,直接重启MySQL服务,什么事务、什么回滚,统统不要了。

但是这样搞风险很大:

  • 数据可能会不一致
  • 其他正常的业务操作也会被打断
  • 万一有重要数据正在操作,可能直接丢了

方案二:等回滚完成

f5543c858f7edc2e0238a61283591fc6
f5543c858f7edc2e0238a61283591fc6

第二种方法就是躺平,老老实实等回滚完成。

我们讨论了一下,最终还是选择了等待。毕竟生产数据不能出问题,宁可慢一点也不能冒险。

结果等了足足1个多小时,回滚才完成,系统速度也恢复了正常。

为什么会这么慢

这次事故让我对大批量update有了更深的认识。

我们这个表有500万条数据,同事这次update了100多万条。这种规模的操作为什么会把整个数据库拖慢呢?

最直接的原因:锁!

MySQL在执行update的时候会给相关的行加锁,100万条数据就是100万把锁。其他所有想要查询这些数据的操作都得排队等着。

还有个更要命的:回滚!

当我们kill掉那个update语句后,MySQL不会简单地停止,而是要把已经修改的数据全部撤销回去。你想想,如果已经update了50万条,那么回滚的时候就要撤销50万条,这个过程可能比原来的update还要慢!

再加上索引更新

每次数据修改,相关的索引也要跟着改。我们这个表上有好几个索引,等于是一次update要同时维护多个索引,CPU和磁盘IO都吃得很紧。

正确的姿势

这次事故之后,我专门研究了一下大批量数据更新的正确做法。如果真的需要更新几百万条数据,应该怎么搞?

分批次处理

最重要的原则:小步快跑

不要一次性update几百万条,要分批次来:

代码语言:sql
复制
-- 每次更新1万条,然后暂停一下
UPDATE your_table
SET column1 = new_value
WHERE condition
LIMIT 10000;

-- 暂停几秒钟,让其他查询有机会执行
-- 然后继续下一批

这样做的好处:

  • 每次只锁1万条数据,影响范围小
  • 其他查询能见缝插针地执行
  • 即使出问题,回滚的代价也小

选对时间

千万别在业务高峰期搞大批量操作!

我们这次就是踩了这个坑,下午2-3点正好是用户活跃时间,结果把所有用户都影响了。

以后这种操作:

  • 凌晨2-5点执行(用户最少的时候)
  • 周末执行(业务量相对较少)
  • 节假日执行(但要确保有人值班)

提前沟通

这种大批量操作不能偷偷摸摸搞,要提前通知:

  • 告诉产品经理可能会有短暂影响
  • 让运维同学准备应急方案
  • 前端同学做好异常处理

我们现在制定了规范:超过10万条数据的操作必须提前申请

后续优化

这次事故之后,我也趁机优化了一下MySQL的配置。有两个参数平时大家可能不太关注,但我觉得挺重要的:

调大缓冲池

代码语言:sql
复制
SET GLOBAL innodb_buffer_pool_size = 8G;

这个参数就是告诉MySQL用多少内存来缓存数据。设置大一点,更多数据能放在内存里,读写速度会快很多。

我们服务器有16G内存,我给MySQL分了8G。这样大部分热点数据都能缓存在内存里,不用频繁读磁盘。

调大IO容量

代码语言:sql
复制
SET GLOBAL innodb_io_capacity = 2000;

这个参数控制MySQL每秒能做多少IO操作。如果你的磁盘性能比较好(比如SSD),可以调大这个值,让MySQL更充分地利用磁盘性能。

我们用的是SSD,IOPS挺高的,所以调到了2000。这样在做大批量操作的时候,MySQL能更快地把数据写到磁盘上。

这次事故的教训

不能随便搞大批量操作

几百万条数据的update,这在生产环境就是个定时炸弹。我们现在规定:

  • 超过10万条的操作必须申请
  • 大批量操作只能在凌晨执行
  • 执行前必须通知所有相关人员

权限要收紧

以前我们对数据库权限管得比较松,开发人员基本都有线上数据库的访问权限。现在看来这样不行,得分级管理:

  • 普通开发只能查询,不能修改
  • 大批量操作需要DBA审批
  • 重要操作必须有备份

监控要跟上

如果我们有完善的监控,这种情况应该能更早发现。我们现在增加了:

  • 慢查询告警(超过5秒就告警)
  • 长事务监控(超过30秒就提醒)
  • 锁等待监控

应急预案要准备

这次我们就是没准备,临时讨论要不要重启,耽误了不少时间。现在我们有了标准流程:

  • 发现大事务先评估影响
  • 能等就等,不能等就kill
  • 重启是最后手段

总的来说,这次虽然闹得挺大,但也让我们的数据库管理更规范了。现在回想起来,当时那个场景还挺搞笑的,一群人在群里讨论为什么这么慢,结果是有人在后台偷偷update几百万条数据😂

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 线上突发故障
  • 开始排查
    • 先看看MySQL在忙什么
    • 继续深挖
    • 看看事务到底在干什么
  • 怎么办?
    • 方案一:直接重启MySQL
    • 方案二:等回滚完成
  • 为什么会这么慢
  • 正确的姿势
    • 分批次处理
    • 选对时间
    • 提前沟通
  • 后续优化
    • 调大缓冲池
    • 调大IO容量
  • 这次事故的教训
    • 不能随便搞大批量操作
    • 权限要收紧
    • 监控要跟上
    • 应急预案要准备
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档