MySQL服务器访问量暴增时,最直接的应对策略是:先扩容扛住流量,再定位慢查询和锁等待,同时开启缓存和读写分离,最后针对索引和SQL做深度优化。
访问量暴增时先做这三件事
流量冲进来的时候,服务器可能在几分钟内就从正常状态跌入濒临宕机的深渊,此时没有时间慢慢分析慢查询日志,先稳住局面才是关键。
临时扩容数据库连接数和内存
这一招不是通过增加数据库配置来“根治问题”,而是通过临时提升连接上限来避免数据库直接拒绝服务,登录服务器,执行:
SET GLOBAL max_connections = 500;
这一步只是应急,如果连接数已经接近原来的上限,说明请求积压严重,此时还要同时检查threads_connected和threads_running两个状态变量,如果threads_running居高不下,说明CPU已经过载,单纯增加连接数反而会让性能更差。
快速定位当前卡顿的源头
立刻执行以下命令,看看当前数据库里到底在跑什么:
SHOW PROCESSLIST;
重点看Time列和State列,如果大量连接卡在Waiting for table metadata lock,说明有DDL操作或长事务阻塞了其他查询;如果卡在Sending data,则是典型的大查询或慢查询堆积,此外在performance_schema.events_statements_summary_by_digest表中,可以按执行次数和平均耗时排序,迅速找出排名靠前的SQL语句。
临时关闭非核心查询和定时任务
把这个思路应用于实践时,优先级排序至关重要,建议立刻暂停三类任务:
- 非实时的报表计算
- 大批量的数据导出
- 长时间运行的备份操作
这些操作通常是压倒MySQL的最后一根稻草,一项统计显示,多数情况下服务器崩溃前的最后几分钟,都伴随着几个大事务在同时执行。
慢查询优化是解决问题的根基
流量暴增只是导火索,SQL性能差才是火药,扛过第一波冲击后,必须回头清理这些定时炸弹。
开启慢查询日志定位问题SQL
建议在配置文件my.cnf中设置:
slow_query_log = ON
long_query_time = 1
然后通过mysqldumpslow工具或者pt-query-digest来分析聚合结果。不要只盯着单个慢SQL,重点看执行频率高的SQL累加耗时,这才是吃掉数据库资源的大户。
用EXPLAIN分析执行计划
对定位到的慢查询执行EXPLAIN SELECT ...,需要逐项核查以下关键指标,类型从好到差依次是system、const、eq_ref、ref、range、index、ALL,如果出现ALL(全表扫描)或index(全索引扫描),说明索引设计存在问题。rows列显示扫描行数,实际扫描行数和返回行数差距悬殊,通常代表索引选择性太差。
常见索引失效的实操场景
- 在索引列上使用函数,比如
WHERE DATE(create_time) = '2026-01-01',这会导致索引完全失效,应改为范围查询create_time >= '2026-01-01' AND create_time < '2026-01-02' - 隐式类型转换,比如字符串字段直接与数字比较,会导致MySQL放弃索引
- 前导模糊匹配,比如
LIKE '%关键词',这种写法无法走索引 - 联合索引不满足最左前缀原则
三种常用的索引优化手段
- 为高频WHERE条件的列建立单列索引
- 为多条件组合查询建立联合索引,把区分度高的列放在最左边
- 给大字段或者超长字符串列设计前缀索引,比如
ALTER TABLE articles ADD INDEX idx_title (title(20))
行业共识认为,索引优化能解决MySQL访问量暴增场景中大概70%的性能问题,这个数字不是凭空来的,看看线上慢查询日志中的SQL类型就能明白。
MySQL缓存配置与Redis分层
数据库扛不住往往是因为重复查询太多,相同的数据被反复从磁盘读出,白白消耗I/O和CPU。
调整InnoDB缓冲池与查询缓存
具体操作时,先看清版本再动手,MySQL 5.7及以下版本还附带一个查询缓存功能,但在高并发写入场景下,开启query_cache_type反而可能引发全局锁竞争,如果还在用这个版本,建议直接关闭查询缓存,把内存留给InnoDB缓冲池,统计显示,MySQL 8.0已经彻底移除了查询缓存,证明这个机制弊大于利。
InnoDB缓冲池的设置原则是:在物理内存允许的范围内尽量调大,如果服务器内存是64GB且主要跑MySQL,可以把innodb_buffer_pool_size设置为40GB到48GB,让更多热数据常驻内存。
Redis做缓存层分担读压力
这是推荐方案中最根本的路径,MySQL擅长的是事务和一致性,而高并发读流量应该让Redis来扛,具体设计逻辑如下:
- 对热点数据(比如用户信息、商品详情)使用“先读Redis,未命中再读MySQL”的模式
- 写操作时先更新数据库,再删除Redis中的对应键,保证最终一致性
- 对访问频率极高的数据设置过期时间,避免无效缓存堆积
提到Redis时也要思考一个问题:如果访问量增长速度极快,Redis本身的连接数也会成为瓶颈,此时应当引入Redis Cluster进行水平扩展,而不是让单个Redis节点承受全部读压力。
MySQL读写分离与分库分表实战
当单库的读写压力都居高不下时,拆库是绕不开的方向。
配置主从复制和读写分离
主从复制架构的思路很简单:主库只处理写操作和事务,从库分担所有读操作,通过中间件(如ProxySQL、MyCat或ShardingSphere)实现流量分发,以ProxySQL为例,核心是配置两组路由规则,写请求走主库,读请求轮询或按权重分发到多个从库。
实操中需要注意:主从延迟是一个客观现实,不能假设它永远为零,对于要求强一致性的业务(比如订单状态查询),必须强制走主库,可以在中间件层设置查询级别规则,也可以在最外层业务代码中设置路由策略,确保一致性要求高的读请求直连主库。
分库分表的应用边界
分库分表是在读写分离之后才需要考虑的方案,如果读写分离已经部署到位,但单表数据量仍在快速增长,再考虑水平拆分,分片键的选择非常重要,它直接决定后续查询是路由到单库还是全库扫描,在实际拆分规划中,建议预留足够的空间,因为后续重新分片的数据迁移成本和大规模重构风险都非常高。
数据库连接池与参数调优
应用层连接池配置不当导致的悲剧比比皆是,一个常见的致命配置是这样的:连接池最大连接数设为200,而MySQL的max_connections
只有151,流量一上来,应用层立即报Too many connections。
推荐做法:将连接池最大连接数设为50到100(根据应用实例数计算),MySQL侧max_connections设为500到1000,预留出管理连接和后台任务的空间,同时设置wait_timeout和interactive_timeout为较短时间(比如60秒),避免空闲连接堆积。
MySQL服务器访问量暴增后的长效监控
解决完眼前的问题,需要建立监控机制来防止下次暴增时手足无措。
部署基础监控指标
需要重点盯防三个维度的指标:
- QPS(每秒查询数)和TPS(每秒事务数):用
SHOW GLOBAL STATUS周期性采样 - InnoDB行锁等待:针对热点行更新频繁的业务,关注
innodb_row_lock_waits的累计值 - 临时表创建速率:
Created_tmp_disk_tables飙升往往意味着排序或分组操作没有走索引
预留服务器冗余容量
从成本控制角度出发,数据库服务器的CPU使用率日常控制在20%以下比较理想,这样在流量暴增时,服务器还有足够的余量来应对,而不是一开始就陷入资源争抢。
常见问题解答
MySQL服务器CPU瞬间飙到100%怎么办
先别急着重启,立即执行SHOW PROCESSLIST,找到所有State为Sending data或Copying to tmp table的线程,记录下对应的SQL,如果连接数太多导致无法操作,可以先把max_connections临时调大,或者直接通过kill命令结束长时间运行的查询线程,终止掉大查询后,CPU资源会在几秒内回落,这时再分析具体是什么SQL导致了CPU过载。
MySQL连接数过多怎么解决
连接数过多的本质是请求处理不过来,而不是连接本身太多,先查看SHOW VARIABLES LIKE 'max_connections'确认当前上限,随后查看SHOW STATUS LIKE 'Threads_connected'的实际连接数,解决路径分三步:第一步排查应用层连接池是否泄露,第二步关闭skip-name-resolve减少DNS解析开销,第三步检查是否存在长时间未提交的事务阻塞了线程回收,最后再考虑调大max_connections作为兜底方案。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/713634.html




