在 Linux 服务器上将 Redis 配置为 MySQL 的缓存层(即「缓存穿透/击穿/雪崩防护 + 数据一致性」的读写缓存架构),Redis 本身不自动与 MySQL 同步,需由应用层(或中间件)主动管理。以下是完整、生产可用的实践方案:
✅ 一、前提准备
-
安装并启动 Redis 和 MySQL
# Ubuntu/Debian sudo apt update && sudo apt install redis-server mysql-server sudo systemctl enable redis-server mysql sudo systemctl start redis-server mysql # 检查 Redis 状态(默认监听 6379,建议绑定内网地址) redis-cli ping # 应返回 "PONG" -
配置 Redis(关键安全与性能优化)
编辑/etc/redis/redis.conf:bind 127.0.0.1 192.168.1.100 # 仅允许本地+内网访问(禁止 0.0.0.0) protected-mode yes requirepass your_strong_password # 强制密码(生产必需!) maxmemory 2gb # 根据内存设置上限 maxmemory-policy allkeys-lru # 或 volatile-lru(推荐带 TTL 的 key) appendonly yes # 开启 AOF 持久化(保障数据不丢失) save "" # 关闭 RDB(避免 fork 阻塞,AOF 足够)重启生效:
sudo systemctl restart redis-server
✅ 二、缓存策略设计(核心逻辑)
| 场景 | 推荐策略 | 说明 |
|---|---|---|
| 读操作 | Cache-Aside(旁路缓存) | 应用先查 Redis → 命中则返回;未命中则查 MySQL → 写入 Redis(带 TTL)→ 返回 |
| 写操作 | Write-Through 或 Write-Behind?❌ 不推荐! ✅ 推荐:Write-Through + 延迟双删 |
先更新 MySQL → 删除 Redis 中对应 key(非更新!)→ 避免脏数据;删除后加延迟再删一次防主从延迟导致的脏读 |
⚠️ 为什么不直接更新 Redis?
MySQL 主从复制有延迟(ms~s 级),若先更新 Redis 再更新 MySQL,从库同步期间可能被读到旧值 → 强一致性破坏。
✅ 三、代码示例(Python + Flask + redis-py + PyMySQL)
1. 初始化连接
# config.py
import redis
import pymysql
REDIS_CONF = {
"host": "127.0.0.1",
"port": 6379,
"password": "your_strong_password",
"db": 0,
"decode_responses": True, # 自动解码 bytes → str
}
MYSQL_CONF = {
"host": "127.0.0.1",
"user": "app_user",
"password": "secure_pass",
"database": "myapp",
"charset": "utf8mb4",
}
2. 缓存读取(带穿透防护)
import json
import time
from redis import Redis
from pymysql import connect
r = Redis(**REDIS_CONF)
mysql_conn = connect(**MYSQL_CONF)
def get_user(user_id: int):
cache_key = f"user:{user_id}"
# Step 1: 尝试从 Redis 获取
cached = r.get(cache_key)
if cached:
return json.loads(cached)
# Step 2: 缓存未命中 → 查 MySQL
with mysql_conn.cursor() as cur:
cur.execute("SELECT id, name, email FROM users WHERE id = %s", (user_id,))
row = cur.fetchone()
if not row:
# 🔒 缓存空值(防穿透),TTL 5 分钟
r.setex(cache_key, 300, json.dumps(None))
return None
# Step 3: 写入缓存(TTL 30 分钟)
data = {"id": row[0], "name": row[1], "email": row[2]}
r.setex(cache_key, 1800, json.dumps(data))
return data
3. 缓存更新(延迟双删)
def update_user(user_id: int, name: str, email: str):
# Step 1: 更新 MySQL
with mysql_conn.cursor() as cur:
cur.execute(
"UPDATE users SET name = %s, email = %s WHERE id = %s",
(name, email, user_id)
)
mysql_conn.commit()
# Step 2: 立即删除 Redis 缓存
cache_key = f"user:{user_id}"
r.delete(cache_key)
# Step 3: 延迟 500ms 后再次删除(覆盖主从延迟窗口)
import threading
def delayed_delete():
time.sleep(0.5)
r.delete(cache_key)
threading.Thread(target=delayed_delete, daemon=True).start()
✅ 四、进阶增强(生产必备)
| 功能 | 实现方式 | 工具/方案 |
|---|---|---|
| 缓存雪崩防护 | 给不同 key 设置随机 TTL(如 1800 ± 300s) |
random.randint(1500, 2100) |
| 缓存穿透防护 | 布隆过滤器(Bloom Filter)预检违规 ID | pybloom_live 或 RedisBloom 模块 |
| 热点 Key 发现 | 监控 Redis INFO commandstats 中 get 命令频次 |
redis-cli --stat 或 Prometheus + Redis Exporter |
| 自动降级 | 当 Redis 不可用时,跳过缓存直连 MySQL | try/except 包裹 Redis 操作,捕获 ConnectionError |
| 多级缓存 | 本地 Caffeine + Redis(减少网络开销) | Java 用 Caffeine,Python 可用 cachetools |
✅ 五、监控与运维(关键!)
-
实时监控 Redis 健康
# 内存使用率 redis-cli info memory | grep -E "(used_memory_human|maxmemory_human|mem_fragmentation_ratio)" # 命中率(>95% 为佳) redis-cli info stats | grep -E "(keyspace_hits|keyspace_misses)" # 计算:hit_rate = hits / (hits + misses) -
接入 Prometheus + Grafana
- 安装 Redis Exporter
- 监控指标:
redis_memory_used_bytes,redis_cache_hit_ratio,redis_connected_clients
-
定期清理过期 key(可选)
# Redis 会自动惰性删除 + 定期抽样删除,无需手动干预 # 但可检查大 key: redis-cli --bigkeys
❌ 常见错误(务必规避)
| 错误 | 后果 | 正确做法 |
|---|---|---|
SET 替代 SETEX |
缓存永不过期 → 内存爆满、数据陈旧 | 永远用 SETEX 或 SET ... EX |
| 更新 MySQL 后不删缓存 | 读到脏数据 | 写后立即删 + 延迟再删 |
用 INCR/HSET 直接更新缓存值 |
主从延迟导致最终不一致 | 只删,不更新(让下次读触发回源) |
| Redis 密码为空 + 绑定 0.0.0.0 | 服务器被X_X木马劫持 | 必须设密码 + bind 内网 IP |
✅ 总结:最佳实践清单
- ✅ Redis 绑定内网 IP + 密码认证 + AOF 持久化
- ✅ 读:Cache-Aside(查缓存 → 未命中查 DB → 写缓存 + TTL)
- ✅ 写:先改 MySQL → 立即删 Redis → 延迟 500ms 再删一次
- ✅ 所有缓存 key 加前缀(如
user:123,order:456)便于管理 - ✅ 对空查询结果也缓存(短 TTL,防穿透)
- ✅ 监控命中率、内存、连接数,设置告警阈值
💡 最后建议:
初期用 Cache-Aside + 延迟双删已足够稳健;
大流量场景可引入 Canal(监听 MySQL binlog 自动同步 Redis),但复杂度陡增,优先保证应用层逻辑正确。
需要我为你生成:
- ✅ 完整的 Flask 示例项目(含 Docker Compose)
- ✅ Redis + MySQL 主从延迟模拟测试脚本
- ✅ 基于 Lua 的原子化双删脚本(避免并发问题)
欢迎随时提出 👇
云知识CLOUD