Redis 最佳实践总结
一、架构设计最佳实践
1.1 选型决策树
选择部署方案:
1. 数据量是否 > 50GB?
├── 是 → Cluster 集群
└── 否 → 2
2. QPS 是否 > 10 万?
├── 是 → Cluster 集群
└── 否 → 3
3. 是否需要自动故障转移?
├── 是 → 哨兵模式
└── 否 → 主从复制
4. 预算是否充足?
├── 否 → 单机版(开发测试)
└── 是 → 哨兵模式(3 哨兵 +1 主 2 从)
1.2 推荐架构
| 规模 | 架构 | 节点数 | 内存 | QPS |
|---|---|---|---|---|
| 小型 | 单机 | 1 | 4GB | <1 万 |
| 中型 | 主从 | 3 | 8GB | <5 万 |
| 大型 | 哨兵 | 6 | 16GB | <10 万 |
| 超大型 | Cluster | 6+ | 32GB+ | >10 万 |
1.3 Key 设计规范
# 命名规范
业务名:模块名:ID:字段
user:profile:12345:name
order:detail:202511050001:status
# 避免的模式
❌ user_12345_name # 分隔符不统一
❌ User:12345:Name # 大小写不一致
❌ very:long:key:that:wastes:memory:12345 # 过长
# 推荐
✅ user:12345:name # 简洁明了
✅ u:12345:n # 极致压缩(内部系统)
1.4 数据类型选择
| 场景 | 推荐类型 | 理由 |
|---|---|---|
| 简单缓存 | String | 性能最好 |
| 对象存储 | Hash | 可单独修改字段 |
| 计数器 | String+INCR | 原子操作 |
| 队列 | List/Stream | 先进先出 |
| 排行榜 | ZSet | 自动排序 |
| 去重 | Set | 天然去重 |
| UV 统计 | HyperLogLog | 节省空间 |
| 签到 | Bitmap | 极致压缩 |
| 地理位置 | GEO | 内置计算 |
二、性能优化最佳实践
2.1 内存优化
# 内存配置
maxmemory 8gb # 预留 25%
maxmemory-policy allkeys-lru # 推荐淘汰策略
maxmemory-samples 10 # 采样数量
# 压缩优化
hash-max-ziplist-entries 512 # Hash 压缩列表
hash-max-ziplist-value 64
list-max-ziplist-size -2 # List 压缩
set-max-intset-entries 512 # Set 整数集合
zset-max-ziplist-entries 128 # ZSet 压缩
zset-max-ziplist-value 64
内存监控:
# 关键指标
INFO memory | grep used_memory_human
INFO memory | grep mem_fragmentation_ratio
# 告警阈值
# 内存使用率 > 80%:告警
# 碎片率 > 1.5:需要整理
# 淘汰次数 > 1000/s:需要扩容
2.2 命令优化
避免的命令:
# ❌ 危险命令(阻塞主线程)
KEYS * # 使用 SCAN 代替
HGETALL large_hash # 使用 HSCAN 代替
SMEMBERS large_set # 使用 SSCAN 代替
LRANGE list 0 -1 # 限制范围
# ✅ 推荐替代
SCAN 0 MATCH pattern COUNT 1000
HSCAN key 0 COUNT 100
SSCAN key 0 COUNT 100
LRANGE list 0 100
批量操作:
// ❌ 逐个操作(1000 次网络往返)
for (String key : keys) {
jedis.get(key);
}
// ✅ Pipeline(1 次网络往返)
Pipeline pipeline = jedis.pipelined();
for (String key : keys) {
pipeline.get(key);
}
List<Response<String>> results = pipeline.sync();
// ✅ MGET(1 次网络往返)
List<String> values = jedis.mget(keys.toArray(new String[0]));
2.3 连接池优化
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxTotal(50); // 最大连接数
config.setMaxIdle(20); // 最大空闲连接
config.setMinIdle(5); // 最小空闲连接
config.setMaxWaitMillis(3000); // 获取连接超时
config.setTestWhileIdle(true); // 空闲时检测
config.setTimeBetweenEvictionRunsMillis(30000); // 检测间隔
JedisPool pool = new JedisPool(config, host, port, 5000, password);
2.4 持久化优化
# RDB 优化
save 900 1 # 避免过于频繁
save 300 10
save 60 10000
rdbcompression yes # 开启压缩
rdbchecksum yes # 开启校验
# AOF 优化
appendonly yes
appendfsync everysec # 推荐
no-appendfsync-on-rewrite yes # 重写时不 fsync
auto-aof-rewrite-percentage 100 # 重写阈值
auto-aof-rewrite-min-size 256mb
# 混合持久化(推荐)
aof-use-rdb-preamble yes
三、运维监控最佳实践
3.1 关键指标监控
#!/bin/bash
# redis_monitor.sh
# 获取 INFO 信息
INFO=$(redis-cli INFO)
# 内存指标
USED_MEM=$(echo "$INFO" | grep "used_memory:" | cut -d: -f2)
MAX_MEM=$(redis-cli CONFIG GET maxmemory | tail -1)
MEM_PCT=$((USED_MEM * 100 / MAX_MEM))
# 连接指标
CONNECTED=$(echo "$INFO" | grep "connected_clients:" | cut -d: -f2)
BLOCKED=$(echo "$INFO" | grep "blocked_clients:" | cut -d: -f2)
# 性能指标
QPS=$(echo "$INFO" | grep "instantaneous_ops_per_sec:" | cut -d: -f2)
REJECTED=$(echo "$INFO" | grep "rejected_connections:" | cut -d: -f2)
# 持久化指标
RDB_STATUS=$(echo "$INFO" | grep "rdb_last_bgsave_status:" | cut -d: -f2)
AOF_ENABLED=$(echo "$INFO" | grep "aof_enabled:" | cut -d: -f2)
echo "=== Redis 监控 ==="
echo "内存使用:${MEM_PCT}%"
echo "连接数:$CONNECTED (阻塞:$BLOCKED)"
echo "QPS: $QPS"
echo "拒绝连接:$REJECTED"
echo "RDB 状态:$RDB_STATUS"
echo "AOF 开启:$AOF_ENABLED"
# 告警
[ $MEM_PCT -gt 80 ] && echo "⚠️ 内存告警"
[ $CONNECTED -gt 1000 ] && echo "⚠️ 连接数告警"
[ "$RDB_STATUS" != "ok" ] && echo "⚠️ RDB 失败告警"
3.2 慢查询分析
# 慢查询配置
slowlog-log-slower-than 10000 # 10ms
slowlog-max-len 128 # 保留 128 条
# 查看慢查询
SLOWLOG GET 10
# 分析慢查询原因
# 1. 大 Key 操作
# 2. 复杂命令(如集合运算)
# 3. 持久化阻塞
# 4. 内存淘汰
# 定期清理
SLOWLOG RESET
3.3 定期巡检清单
每日检查:
- 服务状态(PING)
- 内存使用率
- 连接数
- 持久化状态
- 复制状态
每周检查:
- 大 Key 扫描(—bigkeys)
- 慢查询分析
- 内存碎片率
- 客户端连接分析
每月检查:
- 性能基准测试
- 备份恢复演练
- 故障转移演练
- 容量规划评估
四、安全最佳实践
4.1 访问控制
# 强密码
requirepass YourStrongPassword123!
# 禁用危险命令
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command CONFIG ""
rename-command KEYS ""
rename-command DEBUG ""
rename-command SHUTDOWN ""
# 绑定内网
bind 192.168.1.100
# 修改端口
port 6380
4.2 ACL 权限(Redis 6.0+)
# 默认用户
user default off
# 只读用户
user readonly on >readonlypass ~* +@read
# 应用用户
user appuser on >apppass ~app:* +@write +@read +INCR
# 监控用户
user monitoruser on >monitorpass ~* +INFO +PING
4.3 网络安全
# 防火墙规则
iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 6379 -j ACCEPT
iptables -A INPUT -p tcp --dport 6379 -j DROP
# TLS 加密(Redis 6.0+)
tls-port 6379
tls-cert-file /etc/redis/tls/redis.crt
tls-key-file /etc/redis/tls/redis.key
tls-ca-cert-file /etc/redis/tls/ca.crt
五、故障处理最佳实践
5.1 常见问题处理
问题 1:内存溢出
# 诊断
INFO memory
MEMORY STATS
# 解决
# 1. 临时增加内存
CONFIG SET maxmemory 16gb
# 2. 分析大 Key
redis-cli --bigkeys
# 3. 清理数据
--scan --pattern "cache:*" | xargs redis-cli unlink
# 4. 调整淘汰策略
CONFIG SET maxmemory-policy allkeys-lru
问题 2:CPU 过高
# 诊断
SLOWLOG GET 10
MONITOR | grep -E "KEYS|SMEMBERS|HGETALL"
# 解决
# 1. 终止问题命令
CLIENT KILL ADDR 192.168.1.100:12345
# 2. 优化慢查询
# 3. 限流
# 应用层限制 QPS
问题 3:主从同步失败
# 诊断
INFO replication
# 解决
# 1. 检查网络
telnet master-ip 6379
# 2. 检查磁盘空间
df -h
# 3. 重新配置主从
SLAVEOF master-ip 6379
# 4. 检查复制积压
CONFIG GET repl-backlog-size
5.2 故障转移演练
# 哨兵故障转移测试
# 1. 模拟主节点故障
redis-cli -h master DEBUG sleep 1000
# 2. 观察哨兵日志
tail -f /var/log/redis/sentinel.log
# 3. 验证新主节点
redis-cli -h new-master INFO replication
# 4. 验证数据完整性
redis-cli keys "*" | wc -l
六、高可用架构
6.1 哨兵模式配置
# sentinel.conf
port 26379
sentinel monitor mymaster 192.168.1.10 6379 2
sentinel auth-pass mymaster password
sentinel down-after-milliseconds mymaster 5000
sentinel failover-timeout mymaster 60000
sentinel parallel-syncs mymaster 1
6.2 Cluster 集群配置
# redis-cluster.conf
bind 0.0.0.0
port 7000
cluster-enabled yes
cluster-config-file nodes-7000.conf
cluster-node-timeout 5000
# 创建集群
redis-cli --cluster create \
192.168.1.10:7000 192.168.1.11:7001 192.168.1.12:7002 \
192.168.1.13:7003 192.168.1.14:7004 192.168.1.15:7005 \
--cluster-replicas 1 --cluster-yes
七、缓存设计最佳实践
7.1 缓存策略
// 旁路缓存(Cache-Aside)
public String get(String key) {
// 1. 查缓存
String value = redis.get(key);
if (value != null) {
return value;
}
// 2. 查数据库
value = db.get(key);
if (value != null) {
// 3. 写缓存
redis.setex(key, 3600, value);
}
return value;
}
// 读写穿透(需要框架支持)
// 缓存命中:直接返回
// 缓存未命中:自动加载并缓存
7.2 缓存问题处理
缓存穿透:
// 解决方案:布隆过滤器
if (!bloomFilter.mightContain(key)) {
return null; // 肯定不存在
}
// 继续查询缓存和数据库
缓存击穿:
// 解决方案:互斥锁
String value = redis.get(key);
if (value == null) {
String lockKey = "lock:" + key;
if (redis.setnx(lockKey, "1", 10)) {
try {
// 双重检查
value = redis.get(key);
if (value == null) {
value = db.get(key);
redis.setex(key, 3600, value);
}
} finally {
redis.del(lockKey);
}
} else {
Thread.sleep(100); // 等待后重试
return get(key);
}
}
缓存雪崩:
// 解决方案:随机过期时间
int expireTime = 3600 + random.nextInt(300);
redis.setex(key, expireTime, value);
// 或者:永不过期 + 异步更新
八、总结
8.1 核心原则
- 简单优先:能用简单方案就不用复杂方案
- 预防为主:提前规划,避免问题发生
- 监控先行:建立完善的监控告警体系
- 安全底线:权限控制、网络隔离
- 持续优化:定期巡检、性能测试
8.2 检查清单
上线前:
- 内存配置合理
- 持久化开启
- 密码设置
- 危险命令禁用
- 监控告警配置
- 备份策略制定
日常运维:
- 服务状态正常
- 内存使用正常
- 连接数正常
- 无慢查询
- 持久化正常
- 复制正常