mysql并发与缓存如何配合使用_mysql性能架构说明

来源:这里教程网 时间:2026-02-28 20:41:35 作者:

MySQL 并发高时缓存容易击穿或雪崩

SELECT
请求并发突增(比如秒杀、榜单刷新),即使加了 Redis 缓存,仍可能因缓存未命中集中打到 MySQL,引发连接数飙升、
Waiting for table metadata lock
Lock wait timeout exceeded
。这不是缓存没用,而是缓存策略和 MySQL 并发控制没对齐。

缓存 key 设计必须包含业务维度隔离(如
"user:profile:<user_id>"</user_id>
),避免全量缓存失效导致穿透
读多写少场景下,禁止用
SELECT ... FOR UPDATE
包裹纯查询逻辑——它会升级为行锁甚至间隙锁,阻塞其他并发 SELECT
高并发读建议用
READ COMMITTED
隔离级别,而非默认的
REPEATABLE READ
,减少 MVCC 版本链长度和间隙锁范围
缓存过期时间(TTL)别设固定值,应加随机偏移(如
300 + random(60)
秒),防止批量失效

MySQL 连接池与缓存更新时机必须同步

应用层用连接池(如 HikariCP、mysql-connector-python 的

pool_size
)时,缓存更新若发生在事务提交前,会导致其他连接查到脏数据;若在事务外异步更新,又可能因事务回滚造成缓存与 DB 不一致。

强一致性要求:缓存更新放在事务提交后(如 Spring 的
@Transactional
+
@CacheEvict
,确保事务成功才触发删除)
最终一致性可接受:用 MySQL binlog 解析(如 Canal、Debezium)监听变更,解耦缓存更新,避免应用层事务复杂度 禁止在事务中执行耗时缓存操作(如 Redis
SET
带大 value 或 pipeline),否则拖慢事务,抬高锁持有时间
连接池最大连接数(
max_pool_size
)需略大于缓存失效时的并发重建请求数,否则重建线程会排队等连接,放大延迟

Query Cache 已废弃,别再依赖它做并发优化

MySQL 8.0 已彻底移除

query_cache_type
和相关变量。即使你用的是 5.7,开启
query_cache_size > 0
也会在高并发下成为瓶颈:每次写入表都会清空该表所有缓存结果,且缓存锁是全局互斥锁,
SELECT
多了反而卡住。

替代方案是应用层显式缓存(Redis/Memcached)+ 主动失效,粒度可控 如果仍用 MySQL 5.7 且无法改架构,至少把
query_cache_type = 0
,关闭它,把资源留给 InnoDB buffer pool
innodb_buffer_pool_size
应设为物理内存的 50%–75%,比任何 Query Cache 都更直接提升并发读性能

缓存穿透时 MySQL 的防护要落在 SQL 层

恶意或异常请求查大量不存在的

id
(如
SELECT * FROM user WHERE id = -12345
),缓存层没命中,直接压到 MySQL。这时光靠缓存布隆过滤器不够,MySQL 本身也得有兜底。

在关键查询上加
WHERE id > 0 AND id  类型的硬约束,避免全表扫描无效值
对高频查询字段(如
user_id
)建唯一索引,让
SELECT ... LIMIT 1
能走索引快速返回空结果,而不是等扫描完才确认无数据
pt-query-digest
定期分析慢日志,重点关注
Rows_examined
高但
Rows_sent
为 0 的查询——这是穿透典型特征
应用层对非法参数(如负 ID、超长字符串)做前置校验,不放行到 DAO 层,比在 MySQL 拦截更省资源
SELECT u.* 
FROM user u 
WHERE u.id = ? 
  AND u.id > 0 
  AND u.status = 'active'
LIMIT 1;

真正难的不是配缓存,是判断哪条 SQL 在并发下会抢锁、哪次缓存失效会连环触发重建、以及 MySQL 的 buffer pool 和连接池怎么跟你的缓存 TTL 数值咬合。这些细节不调参、不看慢日志、不模拟压测,只靠“加 Redis”解决不了问题。

相关推荐