评估 Redis 是否要扩容:什么时候该加内存加机器
核心问题:什么时候该扩容
Redis 作为内存数据库,扩容是运维中最常见的操作之一。太早扩容浪费资源,太晚扩容影响业务。我们需要一个科学的判断标准。
扩容的六个关键信号
信号一:内存使用率超过 80%
> INFO MEMORY
# Memory
used_memory:8589934592 # 8GB
maxmemory:10737418240 # 10GB(上限)
使用率 = 8GB / 10GB = 80%。长期超过 80% 就应该考虑扩容。
为什么是 80%?
10GB × 80% = 8GB 使用量
10GB × 20% = 2GB 剩余空间
20% 的余量用于:
– 瞬时突发写入
– RDB/AOF rewrite 期间的额外内存(写入时复制)
– 内存碎片(jemalloc 的 1.02-1.10 倍实际分配)
– 客户端输出缓冲区
信号二:频繁触发淘汰策略
> INFO STATS
evicted_keys:5000 # 正在淘汰 key
evicted_keys 持续增长,说明内存不够用了,Redis 被迫删除一些 key。
def check_eviction_severity(r):
"""检查淘汰严重程度"""
stats1 = r.info('stats')
time.sleep(60)
stats2 = r.info('stats')
evicted_per_sec = (stats2['evicted_keys'] - stats1['evicted_keys']) / 60
if evicted_per_sec == 0:
return "安全"
elif evicted_per_sec < 10:
return "轻度淘汰"
elif evicted_per_sec < 100:
return "中度淘汰,建议观察"
else:
return "严重淘汰,立即扩容!"
信号三:QPS 达到单机上限
# 当 CPU 使用率持续 80%+ 且 QPS 无法再提升时
redis-cli INFO STATS | grep instantaneous_ops_per_sec
# 单机极限测试(根据服务器性能)
# 4 核 8GB: 约 8-12 万 QPS
# 8 核 16GB: 约 12-18 万 QPS
如果 CPU 已经很满但 QPS 无法满足业务需求,说明需要横向扩容(加实例)。
信号四:延迟显著增加
# 正常情况下
redis-cli --latency -h localhost -p 6379
min: 0, max: 2, avg: 0.19
# 需要警惕的情况
# avg > 1ms 且持续上升,同时 CPU 或内存使用率高
信号五:持久化时间变长
redis-cli INFO PERSISTENCE
rdb_last_bgsave_time_sec:15 # 上次 BGSAVE 耗时
aof_last_rewrite_time_sec:20 # 上次 AOF 重写耗时
这些时间随数据集增大而线性增长。如果 RDB 备份时间已经接近备份周期,说明数据太大了。
信号六:主从同步压力增大
redis-cli INFO REPLICATION
master_repl_offset:5000000000
slave_repl_offset:4950000000 # 差距 50M
如果从库同步延迟持续增大,且不是因为从库性能问题,说明主库的写入量太大,需要扩容了。
决策矩阵
def should_scale_up(r):
"""综合判断是否需要扩容"""
info = {
'memory': r.info('memory'),
'stats': r.info('stats'),
'persistence': r.info('persistence'),
'replication': r.info('replication')
}
reasons = []
score = 0
# 1. 内存使用率
used = info['memory']['used_memory']
maxmem = info['memory']['maxmemory']
if maxmem > 0:
ratio = used / maxmem
if ratio > 0.9:
reasons.append(f"内存使用率 {ratio:.0%} > 90% (紧急)")
score += 5
elif ratio > 0.8:
reasons.append(f"内存使用率 {ratio:.0%} > 80% (预警)")
score += 3
# 2. 淘汰 key
evicted = info['stats']['evicted_keys']
if evicted > 1000:
reasons.append(f"已淘汰 {evicted} 个 key")
score += 5
# 3. 残片率
frag = info['memory']['mem_fragmentation_ratio']
if frag > 2.0:
reasons.append(f"碎片率 {frag:.2f}(过高)")
score += 2
# 4. 持久化时间
bgsave_time = info['persistence'].get('rdb_last_bgsave_time_sec', 0)
if bgsave_time > 60:
reasons.append(f"BGSAVE 耗时 {bgsave_time}s")
score += 2
# 5. 峰值对比
peak = info['memory']['used_memory_peak']
if peak > maxmem * 0.9:
reasons.append(f"内存峰值 {peak/1024/1024:.0f}MB 接近上限")
score += 3
decision = "需要紧急扩容" if score >= 8 else "建议扩容" if score >= 5 else "暂不需要扩容"
return {
'score': score,
'decision': decision,
'reasons': reasons
}
扩容方案
方案一:垂直扩容(升级硬件)
# 简单粗暴:加内存
# 旧:4GB → 新:8GB/16GB/32GB
# 需要注意
# 1. 云服务只需调整规格
# 2. 自建服务器需要关机加内存条
# 3. 大内存重启后 RDB 恢复更慢
# 4. fork 耗时随内存增大而增加
方案二:水平扩容(Redis Cluster)
# 从单机扩容到 3 节点 Cluster
# 每个节点仍用 8GB,但总量变成 24GB
# 新增节点
redis-cli --cluster add-node new-node:6379 existing-node:6379
# 重新分配 slot
redis-cli --cluster rebalance existing-node:6379
# 监控数据迁移
redis-cli --cluster check existing-node:6379
方案三:读写分离
# 如果主要是读压力大(QPS 高但内存不是瓶颈)
# 增加从库
SLAVEOF master_host master_port
# 配置客户端使用从库读
readonly = True # 只读从库
扩容决策检查清单
## 扩容前的自查清单
### 先确认不是代码问题
□ 有没有 BigKey 可以拆分?
□ 有没有过期时间可以设置?
□ 数据结构是否可以优化(String → Hash?)
□ 是否有内存泄漏的代码?
### 再确认真的需要扩容
□ 内存使用率 > 80% 持续超过 1 周?
□ 有 key 被淘汰(evicted_keys > 0)?
□ QPS 接近单机上限?
□ 延迟显著增加?
□ 主从同步延迟增大?
### 选择扩容方案
□ 垂直扩容(升级配置)短期应急
□ 水平扩容(加实例)长期方案
□ 读写分离 读多写少场景
面试要点
- 核心判断标准:内存使用率 > 80% + evicted_keys > 0
- 扩容的两种方式:垂直(升级单机)和水平(加实例)
- 垂直扩容简单但有上限(单机内存上限)
- 水平扩容是更推荐的方案(可线性扩展)
- 扩容前先确认是否可以通过代码优化减少内存消耗
- 峰值内存(used_memory_peak)也是重要参考指标
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容