MySQL 8.0正式移除了原生的查询缓存功能,这意味着原本通过query_cache_type开启的查询缓存机制不再可用。对于很多依赖查询缓存减少重复查询、降低数据库压力的业务来说,需要找到可靠的替代方案,Redis凭借高性能、高可用的特性成为首选替代工具。

MySQL原生查询缓存的局限性
MySQL的查询缓存是将SELECT语句的哈希值作为key,查询结果作为value存储在内存中,相同的查询可以直接返回缓存结果,不需要再次执行SQL。但该机制存在明显缺陷:
- 只要表发生任何写操作,该表相关的所有查询缓存都会失效,对于写频繁的业务缓存命中率极低
- 缓存存储在MySQL进程内存中,会占用数据库本身的内存资源,影响数据库其他组件的运行
- 缓存粒度是整个查询结果,无法做到更细粒度的控制,灵活性较差
Redis替代查询缓存的优势
使用Redis作为查询缓存的替代方案,可以解决原生查询缓存的多数问题:
- Redis是独立的内存数据库,不会占用MySQL的内存资源,性能更稳定
- 可以自定义缓存粒度,既可以缓存整个查询结果,也可以缓存单条记录,灵活性更高
- 支持多种缓存过期策略,也支持主动更新缓存,能适配更多业务场景
- Redis本身支持集群、持久化等特性,可用性远高于原生查询缓存
Redis查询缓存的实现方案
1. 缓存Key设计规范
缓存Key需要唯一标识一条查询请求,建议采用业务模块:查询类型:参数哈希的格式,例如用户模块的按ID查询用户信息的缓存Key可以设计为:
user:query_by_id:123456
其中参数部分如果是多个参数,可以将参数拼接后计算哈希值,避免Key过长。
2. 查询流程设计
完整的查询流程分为以下几步:
- 根据查询条件生成对应的缓存Key
- 尝试从Redis中获取缓存,如果缓存存在直接返回结果
- 如果缓存不存在,执行MySQL查询获取结果
- 将查询结果序列化后存入Redis,并设置合理的过期时间
- 返回查询结果给调用方
3. 代码示例(Python实现)
以下是使用Python的redis和pymysql库实现的完整查询缓存逻辑:
import redis
import pymysql
import json
import hashlib
# 初始化Redis连接
redis_client = redis.Redis(host='127.0.0.1', port=6379, db=0, decode_responses=True)
# 初始化MySQL连接
mysql_conn = pymysql.connect(
host='127.0.0.1',
port=3306,
user='root',
password='test_password',
database='test_db',
charset='utf8mb4'
)
def generate_cache_key(module, query_type, params):
# 拼接参数并计算哈希,生成缓存Key
param_str = '&'.join([f'{k}={v}' for k, v in params.items()])
param_hash = hashlib.md5(param_str.encode()).hexdigest()
return f'{module}:{query_type}:{param_hash}'
def query_with_cache(sql, params, cache_expire=300):
# 生成缓存Key
cache_key = generate_cache_key('db', 'query', {'sql': sql, 'params': params})
# 尝试获取缓存
cache_result = redis_client.get(cache_key)
if cache_result is not None:
return json.loads(cache_result)
# 缓存不存在,执行查询
cursor = mysql_conn.cursor(pymysql.cursors.DictCursor)
cursor.execute(sql, params)
query_result = cursor.fetchall()
cursor.close()
# 存入缓存
redis_client.setex(cache_key, cache_expire, json.dumps(query_result))
return query_result
# 示例调用:查询用户表中id为1的用户信息
sql = 'SELECT * FROM user WHERE id = %s'
params = (1,)
result = query_with_cache(sql, params)
print(result)
4. 缓存更新策略
当数据库数据发生变更时,需要及时更新或删除对应的缓存,避免缓存脏数据:
- 对于写操作(INSERT、UPDATE、DELETE),执行完数据库操作后,删除对应查询条件的缓存Key
- 如果业务允许,可以设置较短的缓存过期时间,降低脏数据的影响范围
- 对于高频更新的数据,可以考虑采用旁路缓存模式,写操作时更新缓存而不是删除缓存
5. 常见问题应对
缓存穿透
指查询不存在的数据,导致每次都穿透到数据库。应对方案:
- 对不存在的查询结果也进行缓存,设置较短的过期时间,比如60秒
- 使用布隆过滤器提前过滤不存在的数据ID
缓存击穿
指某个热点Key过期瞬间,大量请求同时穿透到数据库。应对方案:
- 热点Key设置永不过期,通过后台线程定期更新缓存
- 获取缓存时使用分布式锁,只有一个请求去查询数据库,其他请求等待缓存更新完成
缓存雪崩
指大量缓存同时过期,导致数据库压力骤增。应对方案:
- 缓存过期时间加上随机偏移量,避免同时过期
- 采用多级缓存架构,比如本地缓存+Redis缓存,降低对Redis的依赖
注意事项
在使用Redis替代MySQL查询缓存时,需要注意以下几点:
- 缓存的序列化方式要统一,避免反序列化失败
- 缓存过期时间要根据业务数据的更新频率设置,不要盲目设置过长
- 定期监控Redis的内存使用情况,避免缓存数据过多占用过多内存
- 对于大结果集的查询,不建议全部缓存,可以只缓存ID列表,再分别查询单条记录缓存
MySQLRedisquery_cache_type查询缓存修改时间:2026-06-10 20:45:24