前言
不少开发会遇到这样的现象:单独执行一条sql速度很快,但是压测并发量上来之后,接口响应变慢、数据库cpu持续走高、连接数不断上涨,甚至出现查询超时。
很多人第一反应就是优化sql,但单纯优化语句只能解决一部分问题。mysql并发查询承载能力,是sql索引、事务锁、内核参数、缓存体系、整体架构共同决定的。
本文基于 innodb 引擎(mysql5.7 / 8.0 生产通用),由浅入深梳理可直接落地的优化手段,帮助系统支撑更高并发查询流量。
一、先理解:innodb 高并发基础原理
innodb 依托两大核心机制支撑并发读写:
- mvcc 多版本并发控制:普通select属于快照读,实现无锁查询,读不会阻塞读、读不会阻塞写,这是mysql支撑海量查询的基础。
- 行级锁:正常情况下只锁定被修改的数据行,锁粒度小,相比myisam表锁,写入并发能力大幅提升。
重点提醒:mvcc、行锁是基础保障,但如果使用方式错误,依然会出现大量阻塞、吞吐上不去。
常见并发瓶颈来源:慢sql长期占用工作线程、索引失效引发大量扫描、长事务持有锁、热点行竞争、大量重复查询直接打穿数据库。
二、第一层优化:sql & 索引优化(投入产出比最高)
高并发场景有一条铁律:单条查询耗时越短,系统能够承载的并发越高。查询耗时越长,数据库连接占用时间越久,连接池很快耗尽。
2.1 高频查询必须命中有效索引
严禁高频业务sql出现全表扫描。
- 避免索引字段使用函数运算、隐式类型转换;
- 模糊查询不要使用前置通配符
%关键词; - 多条件查询合理设计联合索引,遵守最左匹配原则。
示例业务场景:
-- 筛选条件 status + verify_idf_id where status = 1 and verify_idf_id = 'xxx'
创建联合索引:
create index idx_status_verify on openapi_price(status, verify_idf_id);
2.2 禁止select *
只查询业务需要的字段,好处:
- 减少回表io;
- 降低网络传输数据量;
- 更容易触发覆盖索引,避免访问主键数据。
2.3 in、分页、排序的坑点
in (常量列表):少量参数可以走range索引;如果in内元素数量巨大,优化器可能放弃索引,建议分批查询;in(子查询):mysql8.0内部会自动做半连接优化,5.7环境下优先使用exists保证稳定性;order by不要对索引列使用函数转换,例如cast(str_id as unsigned),会直接造成索引失效、产生filesort文件排序,高并发下压力巨大;可以将排序逻辑上移至应用内存处理。- 大分页
limit offset,size随着offset增大性能持续衰减,改用主键分页方案。
2.4 及时清理无效慢查询
长期存在的慢查询会持续占用工作线程,并发涌入后迅速形成请求堆积。在线上持续监控慢查询日志,定期优化。
三、第二层优化:事务与锁优化,减少查询阻塞
很多时候查询卡顿不是查询本身慢,而是被写入事务锁阻塞。
3.1 尽可能缩短事务执行时长
事务开启到提交的区间越长,行锁持有时间越久,其他读写请求越容易产生锁等待。
- 不要在事务内执行耗时网络请求、大量查询;
- 事务中只保留必要的dml操作;
- 避免长事务长期不提交。
3.2 区分快照读与当前读
普通select是快照读,不加锁;
如果业务不需要强一致性,不要随意添加select ... for update这类锁定读。大量锁定读会引发激烈锁竞争,严重降低并发能力。
3.3 规避热点行更新
大量并发同时更新同一行数据,会形成串行等待。
方案:业务层做合并、异步化、数据分片,分散热点竞争。
四、第三层优化:mysql内核参数调优
参数调整需要结合服务器内存配置,不要盲目照搬网上模板。
核心关键参数
innodb_buffer_pool_size:innodb最重要参数,缓存索引和数据页。推荐设置为物理内存的50%~70%,足够大的缓冲池能够大幅减少磁盘io,显著提升查询并发。
max_connections:最大连接数,默认偏小。但不要设置过大,连接过多会造成操作系统上下文切换开销上升。一般业务设置 500~2000,配合应用侧连接池使用。
innodb_read_io_threads / innodb_write_io_threads:读写io线程,提升磁盘并发读写能力,多核机器可以适当调高。
innodb_flush_log_at_trx_commit:数据安全与性能平衡:
- 每次事务刷盘,安全性最高,性能最低;
- 每秒刷一次磁盘,崩溃可能丢失1秒数据,查询与写入并发性能明显提升。
生产调整前评估数据丢失风险。
sort_buffer_size、join_buffer_size:不要全局调大,过大容易造成内存耗尽;存在大量排序、关联查询时按需优化sql优先,而不是单纯增大缓冲区。
五、第四层优化:引入缓存,降低数据库查询压力
数据库的并发承载能力存在上限,最有效的手段是减少打到mysql的请求量。
5.1 应用层缓存(redis)
对于变更频率低、查询量大的基础数据、配置、字典、接口文档信息,将查询结果缓存至redis。
流量优先命中缓存,避免频繁查询数据库。
5.2 合理使用查询缓存
mysql8.0已经移除query cache,不要依赖;5.7版本也不推荐开启,频繁更新的表会让缓存整体失效。
5.3 本地内存缓存
热点静态数据可以在应用内存中缓存,进一步减少跨网络缓存请求。
六、第五层优化:架构层面横向扩容
单台mysql无论怎么调优,硬件上限无法突破。流量持续上涨后需要架构升级。
读写分离:一主多从,所有查询请求路由到从库,主库只负责写入,分担查询压力。
注意:从库存在数据同步延迟,强一致性业务查询依然访问主库。
分库分表:单表数据量达到千万级别,索引、查询性能持续下滑。按照业务维度分片,分散单表查询压力,提升整体并发吞吐。
业务隔离:核心业务、非核心业务使用独立数据库实例,避免非核心报表、导出任务抢占核心查询资源。
七、线上排查并发性能问题的手段
遇到并发查询卡顿,按顺序排查:
show processlist查看是否存在大量长时间执行的sql、锁等待;explain验证高频查询是否正常走索引;- 监控指标:cpu使用率、磁盘io、连接数、锁等待时长、慢查询数量;
- 查看
innodb_status,观察行锁等待、事务情况; - 核对缓冲池命中率,判断是否存在大量磁盘读取。
八、总结
提升mysql并发查询能力可以按照优先级落地:
- 优化sql与索引,缩短单条查询耗时(最高优先级);
- 规范事务写法,减少锁竞争与阻塞;
- 合理调整innodb核心参数,充分利用服务器硬件资源;
- 增加多级缓存,削减直达数据库的请求数量;
- 流量持续增长时,通过读写分离、分库分表实现架构扩容。
并发优化不存在万能配置,一切优化动作都需要结合业务真实流量、数据特征持续观测调整。优先保证基础sql质量,再考虑架构扩容,避免盲目加机器治标不治本。
到此这篇关于mysql提高并发查询能力的全方位实战优化指南的文章就介绍到这了,更多相关mysql并发查询优化内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论