单库瓶颈分析:连接数打满、磁盘IO瓶颈、慢查询拖垮全服
说实话,做游戏后端这些年,我见过太多项目死在「单库」这两个字上。
很多团队一开始觉得,一个MySQL实例够用了。用户量不大,数据量也不大,干嘛要搞分库分表?嗯,这个想法在日活1万以下确实没问题。但一旦用户量上来,单库的瓶颈就会像多米诺骨牌一样,一个接一个倒下来。
今天我就拿一个真实案例复盘,带你看清楚单库是怎么被拖垮的。
1. 连接数打满:第一个倒下的骨牌
先说说连接数的问题。
MySQL默认的最大连接数一般是151。你可能会觉得,151个连接不少了啊?但你要知道,游戏服务器可不是只有一个进程在连数据库。
我经历过的一个项目,上线第三天就出事了。玩家反馈登录超时,充值不到账,公会数据读不出来。我一看监控,MySQL连接数直接飙到150+,新连接全部排队等待。
为什么会这样?
我们当时有8个游戏服务器进程,每个进程维护了一个20连接的连接池。8×20=160,已经超过151了。再加上管理后台、运营工具、日志采集程序……连接数不爆才怪。
我当时紧急处理的方式是:
- 先把连接池大小从20降到15
- 把管理后台的连接池从10降到3
- 临时把max_connections调到300
但这只是治标不治本。你想想看,连接数只是表象,真正的问题是——所有请求都挤在一个数据库上。
2. 磁盘IO瓶颈:读写争抢的噩梦
连接数的问题刚稳住,磁盘IO又出事了。
游戏服务器的数据库,读写比例大概在7:3左右。读多写少,但问题是——写操作往往很重。
比如玩家下线时,要保存背包数据、任务进度、战斗记录、社交关系……这些数据可能分布在十几张表里。一次下线操作,可能要产生几十个写请求。
我记得有一次,晚上8点高峰期,磁盘IO等待时间直接飙到5000毫秒以上。什么意思?就是数据库发一个磁盘读写请求,要等5秒才能拿到结果。
你想想看,玩家点一下「强化装备」,5秒后才看到结果。这游戏还能玩吗?
磁盘IO瓶颈的典型表现:
- 慢查询日志里全是等待IO的条目
- iostat显示磁盘利用率接近100%
- 数据库的QPS(每秒查询数)突然下降
说白了,磁盘IO就是数据库的「咽喉」。一旦这里堵了,整个系统都得跟着慢下来。
3. 慢查询拖垮全服:最后一根稻草
连接数和磁盘IO的问题还没完全解决,慢查询又来了。
慢查询这个东西,平时可能没什么感觉。但一旦并发上来,一个慢查询就能拖垮整个数据库。
我印象最深的一次——某个排行榜查询,SQL写得特别随意:
SELECT * FROM player_score
ORDER BY score DESC
LIMIT 100
看起来很简单对吧?但问题是player_score表有500万行数据,而且score字段没有索引。每次执行这个查询,MySQL都要做一次全表扫描,然后排序。
这个查询平时执行要3秒。高峰期并发100个这样的查询,数据库直接卡死。所有其他查询都在排队等这个慢查询释放资源。
慢查询的典型特征:
- 执行时间超过1秒
- 扫描行数巨大(几十万甚至上百万)
- 没有使用索引,或者索引选择错误
我当时排查慢查询的方法:
- 开启慢查询日志,设置long_query_time=1
- 用pt-query-digest分析慢查询日志
- 找到执行次数最多、总耗时最长的SQL
- 用EXPLAIN分析执行计划
嗯,这里要注意——慢查询日志本身也会消耗IO。高峰期不要开太细的日志级别,否则会雪上加霜。
4. 三个瓶颈的连锁反应
这三个问题不是孤立的。它们会互相放大。
| 问题 | 直接后果 | 连锁反应 |
|---|---|---|
| 连接数打满 | 新请求被拒绝 | 玩家重试→更多连接请求→更严重的打满 |
| 磁盘IO瓶颈 | 读写延迟飙升 | 查询变慢→连接持有时间变长→连接数更容易打满 |
| 慢查询 | 单查询耗时过长 | 占用连接和IO→其他查询排队→整体延迟爆炸 |
你看,这三个问题就像三个环,环环相扣。任何一个环节出问题,都会把其他环节拖下水。
5. 复盘总结:单库的极限在哪里?
经过这次事故,我总结了一个单库的「安全边界」:
- 连接数:不要超过最大连接数的60%
- 磁盘IO:利用率不要超过70%
- 慢查询:单个查询超过500ms就要优化
- 数据量:单表超过500万行就要考虑分表
当然,这些数字不是绝对的。不同的硬件配置、不同的业务场景,边界值会有差异。但有一点是确定的——
单库不是银弹。它只是一个起点。
当你的游戏用户量增长到一定程度,单库的瓶颈一定会出现。与其等到事故发生了再手忙脚乱,不如提前做好分库分表的规划。
下一章,我会详细讲分库分表的策略和实战方案。到时候你会看到,分库分表不是「要不要做」的问题,而是「什么时候做」的问题。
嗯,今天就先聊到这里。记住这个案例,它可能会帮你避免一次线上事故。