在 PHP 项目里,Redis 可以说是用得最广泛的 NoSQL 缓存数据库了。可不少团队在生产环境部署它时踩了不少坑:内存配置不当引发 OOM、持久化策略错误导致数据丢失、集群架构不合理拖垮性能、安全配置缺失被扫描攻击。
本文从实际生产经验出发,系统梳理 Redis 在生产环境中的部署避坑要点,覆盖内存配置、持久化方案、集群架构、安全加固与监控告警等方面。
1 内存配置
1.1 最大内存配置
# 查看当前最大内存配置
redis-cli CONFIG GET maxmemory
# 设置最大内存(字节)
redis-cli CONFIG SET maxmemory 10737418240 # 10GB
# 临时设置(重启失效)
redis-server --maxmemory 10gb
# 永久设置(配置文件)
# redis.conf
maxmemory 10gb
内存单位说明:
1k => 1000 bytes
1kb => 1024 bytes
1m => 1000000 bytes
1mb => 1024*1024 bytes
1g => 1000000000 bytes
1gb => 1024*1024*1024 bytes
1.2 内存淘汰策略
当 Redis 内存达到 maxmemory 时,会根据配置的淘汰策略删除键值。
# 查看当前淘汰策略
redis-cli CONFIG GET maxmemory-policy
# 设置淘汰策略
redis-cli CONFIG SET maxmemory-policy allkeys-lru
可选策略:
| 策略 |
说明 |
适用场景 |
| noeviction |
不淘汰,返回错误 |
数据不允许丢失 |
| allkeys-lru |
所有键使用 LRU 淘汰 |
通用缓存场景 |
| allkeys-lfu |
所有键使用 LFU 淘汰 |
热点数据场景 |
| allkeys-random |
所有键随机淘汰 |
随机淘汰场景 |
| volatile-lru |
已设置 TTL 的键使用 LRU |
混合使用 |
| volatile-lfu |
已设置 TTL 的键使用 LFU |
混合使用 |
| volatile-random |
已设置 TTL 的键随机淘汰 |
混合使用 |
| volatile-ttl |
已设置 TTL 的键按 TTL 淘汰 |
保留热点数据 |
LRU 与 LFU 的区别:
- LRU (Least Recently Used):最近最少使用,淘汰最久未访问的键
- LFU (Least Frequently Used):最不经常使用,淘汰访问频率最低的键
# volatile-lru 适合的场景:
# 既有缓存数据又有持久数据的混合应用
# 设置了 TTL 的数据希望优先淘汰
# allkeys-lru 适合的场景:
# 所有数据都是缓存,希望优先保留热点数据
1.3 内存碎片
Redis 使用 glibc 的内存分配器,会产生内存碎片。
# 查看内存碎片率
redis-cli INFO memory | grep mem_fragmentation_ratio
# 手动整理碎片(Redis 4.0+)
redis-cli MEMORY PURGE
# 重启 Redis 也会清理碎片
内存碎片率说明:
- 1.0 ~ 1.5:正常范围
- > 1.5:碎片较多,可能需要整理
- < 1.0:使用了 swap 或物理内存不足
减少碎片的建议:
- 使用 Jemalloc 内存分配器(Redis 默认)
- 合理设置 maxmemory,避免频繁淘汰
- 使用 Redis 4.0+ 的主动碎片整理
# 启用主动碎片整理(Redis 4.0+)
redis-cli CONFIG SET activedefrag yes
# 查看碎片整理统计
redis-cli INFO memory | grep mem_fragmentation_ratio
1.4 内存监控
# 查看内存使用详情
redis-cli INFO memory
# 输出示例
# used_memory: 1073741824 # Redis 实际使用的内存
# used_memory_human: 1.00G
# used_memory_rss: 1258291200 # 操作系统实际分配的物理内存
# used_memory_rss_human: 1.17G
# used_memory_peak: 2147483648 # 内存使用峰值
# used_memory_peak_human: 2.00G
# mem_fragmentation_ratio: 1.17 # 内存碎片率
# mem_allocator: jemalloc-5.2.1 # 内存分配器
# 监控脚本
#!/bin/bash
USED=$(redis-cli INFO memory | grep used_memory | awk -F: '{print $2}')
PEAK=$(redis-cli INFO memory | grep used_memory_peak | awk -F: '{print $2}')
MAX=$(redis-cli CONFIG GET maxmemory | tail -1)
echo "Used: $((USED/1024/1024)) MB"
echo "Peak: $((PEAK/1024/1024)) MB"
echo "Max: $((MAX/1024/1024)) MB"
echo "Usage: $(echo "scale=2; $USED*100/$MAX" | bc)%"
2 持久化配置
2.1 RDB 持久化
RDB 是 Redis 的定时快照备份,生成紧凑的二进制文件。
# 手动触发 RDB
redis-cli BGSAVE
# 或
redis-cli SAVE # 同步保存,会阻塞 Redis
# 查看最近一次 RDB 保存时间
redis-cli DEBUG SLEEP 1 && redis-cli LASTSAVE
# 查看子进程状态
redis-cli INFO persistence
# rdb_changes_since_last_save: 0
# rdb_last_save_time: 1609459200
# rdb_last_bgsave_status: ok
# rdb_last_bgsave_time_sec: 1
RDB 配置:
# redis.conf
# 多久触发一次 RDB
# 900秒内至少1个key变化
save 900 1
# 300秒内至少10个key变化
save 300 10
# 60秒内至少10000个key变化
save 60 10000
# 关闭 RDB(如果只用作缓存)
save ""
# RDB 文件名
dbfilename dump.rdb
# RDB 文件存放目录
dir /var/lib/redis
# bgsave 失败时禁止写入
stop-writes-on-bgsave-error yes
# 是否压缩 RDB 文件
rdbcompression yes
# 是否校验 RDB 文件
rdbchecksum yes
RDB 优点:
- 文件紧凑,适合备份和灾难恢复
- 恢复大数据集时比 AOF 快
RDB 缺点:
- 可能丢失最后一次快照后的数据
- fork() 操作在数据量大时可能导致 Redis 短暂卡顿
2.2 AOF 持久化
AOF 是 Redis 的增量日志,记录每个写操作。
# 查看 AOF 状态
redis-cli INFO persistence
# aof_enabled: 1
# aof_current_size: 1048576
# aof_base_size: 1048576
# aof_pending_rewrite: 0
# aof_buffer_length: 0
# aof_rewrite_buffer_length: 0
# aof_pending_bio_fsync: 0
# aof_last_bgrewrite_status: ok
# aof_last_write_status: ok
AOF 配置:
# redis.conf
# 开启 AOF
appendonly yes
# AOF 文件名
appendfilename "appendonly.aof"
# AOF 写入策略
appendfsync everysec # 每秒同步,最多丢失1秒数据(推荐)
# appendfsync always # 每次写入同步,最安全但最慢
# appendfsync no # 由操作系统决定何时同步,最快但最不安全
# AOF 重写配置
auto-aof-rewrite-percentage 100 # 文件比上次重写大100%时触发
auto-aof-rewrite-min-size 64mb # 文件至少达到64MB才触发
# AOF 重写失败时停止写入
aof-load-truncated yes
# 启用 RDB-AOF 混合持久化(Redis 7.0+)
aof-use-rdb-preamble yes
AOF 优点:
- 数据安全性更高,最多丢失一个写入周期
- AOF 文件是追加写入,不会产生随机写
AOF 缺点:
2.3 混合持久化(Redis 4.0+)
# 启用混合持久化
aof-use-rdb-preamble yes
混合持久化会在 AOF 重写时使用 RDB 格式存储数据,然后在后面追加增量 AOF 指令。这样既保证了快速恢复,又保证了数据不丢失。
恢复优先级:
- 如果存在 AOF 文件,优先使用 AOF 恢复
- 如果 AOF 被关闭或不完整,使用 RDB 恢复
2.4 生产环境持久化建议
# 生产环境推荐配置
# 内存配置
maxmemory 8gb
maxmemory-policy allkeys-lru
# RDB 配置
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
# AOF 配置
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
aof-load-truncated yes
aof-use-rdb-preamble yes
3 RDB 与 AOF 备份策略
3.1 备份脚本
#!/bin/bash
# filename: redis_backup.sh
# Redis 备份脚本
BACKUP_DIR="/data/redis_backup"
REDIS_HOST="127.0.0.1"
REDIS_PORT="6379"
REDIS_PASSWORD="your_redis_password"
DATE=$(date +%Y%m%d_%H%M%S)
# 创建备份目录
mkdir -p $BACKUP_DIR
# 获取 Redis 配置
REDIS_DATA_DIR=$(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD CONFIG GET dir | tail -1)
REDIS_DBFILE=$(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD CONFIG GET dbfilename | tail -1)
REDIS_AOFFILE=$(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD CONFIG GET appendfilename | tail -1)
# 执行 BGSAVE
echo "Triggering BGSAVE..."
redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD BGSAVE
# 等待 RDB 保存完成
while [ $(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD LASTSAVE) -eq $(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD LASTSAVE) ]; do
sleep 1
done
# 复制 RDB 文件
if [ -f "$REDIS_DATA_DIR/$REDIS_DBFILE" ]; then
cp $REDIS_DATA_DIR/$REDIS_DBFILE $BACKUP_DIR/dump_$DATE.rdb
echo "RDB backup: $REDIS_DBFILE"
fi
# 复制 AOF 文件
if [ -f "$REDIS_DATA_DIR/$REDIS_AOFFILE" ]; then
cp $REDIS_DATA_DIR/$REDIS_AOFFILE $BACKUP_DIR/appendonly_$DATE.aof
echo "AOF backup: $REDIS_AOFFILE"
fi
# 压缩备份文件
cd $BACKUP_DIR
tar czf redis_backup_$DATE.tar.gz *.rdb *.aof 2>/dev/null
rm -f *.rdb *.aof
# 删除 7 天前的备份
find $BACKUP_DIR -name "redis_backup_*.tar.gz" -mtime +7 -delete
echo "Backup completed: redis_backup_$DATE.tar.gz"
ls -lh $BACKUP_DIR
3.2 恢复脚本
#!/bin/bash
# filename: redis_restore.sh
# Redis 数据恢复脚本
BACKUP_FILE=$1
REDIS_HOST="127.0.0.1"
REDIS_PORT="6379"
REDIS_PASSWORD="your_redis_password"
if [ -z "$BACKUP_FILE" ]; then
echo "Usage: $0 <backup_file.tar.gz>"
exit 1
fi
if [ ! -f "$BACKUP_FILE" ]; then
echo "Error: Backup file not found"
exit 1
fi
# 停止 Redis
echo "Stopping Redis..."
systemctl stop redis
# 备份当前数据
REDIS_DATA_DIR=$(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD CONFIG GET dir | tail -1)
if [ -f "$REDIS_DATA_DIR/dump.rdb" ]; then
mv $REDIS_DATA_DIR/dump.rdb $REDIS_DATA_DIR/dump.rdb.bak
fi
if [ -f "$REDIS_DATA_DIR/appendonly.aof" ]; then
mv $REDIS_DATA_DIR/appendonly.aof $REDIS_DATA_DIR/appendonly.aof.bak
fi
# 解压备份文件
cd $REDIS_DATA_DIR
tar xzf $BACKUP_FILE
# 启动 Redis
echo "Starting Redis..."
systemctl start redis
# 验证数据
KEY_COUNT=$(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD DBSIZE)
echo "Restored keys: $KEY_COUNT"
# 如果数据不对,恢复备份
if [ "$KEY_COUNT" -eq 0 ]; then
echo "Warning: No data restored, restoring from .bak files"
mv $REDIS_DATA_DIR/dump.rdb.bak $REDIS_DATA_DIR/dump.rdb
systemctl restart redis
fi
4 安全加固
4.1 密码认证
# 设置密码(临时)
redis-cli CONFIG SET requirepass "your_strong_password"
# 设置密码(永久,redis.conf)
requirepass your_strong_password
# 使用密码连接
redis-cli -a your_strong_password
redis-cli -h 127.0.0.1 -p 6379 -a your_strong_password
# 使用密码认证
redis-cli
AUTH your_strong_password
# 设置多个密码(ACL,Redis 6.0+)
ACL SETUSER alice on >password ~cached:* +get +set +del +exists
4.2 绑定地址
# 只监听本地
bind 127.0.0.1
# 监听特定 IP
bind 192.168.1.100
# 监听多个 IP
bind 127.0.0.1 192.168.1.100
# 监听所有接口(危险!)
bind 0.0.0.0 # 禁止生产环境使用
4.3 禁用危险命令
# redis.conf
# 禁用危险命令
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command DEBUG ""
rename-command KEYS ""
# 或者重命名为不易猜测的名字
rename-command FLUSHDB FLUSHDB_RENAMED
rename-command KEYS KEYSCMD
4.4 限制连接数
# 最大连接数
maxclients 10000
# TCP backlog
tcp-backlog 511
# 超时设置(客户端空闲多久断开)
timeout 300
# 保活设置
tcp-keepalive 300
4.5 防火墙设置
# iptables 限制访问
iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 6379 -j ACCEPT
iptables -A INPUT -p tcp --dport 6379 -j DROP
# 如果使用云服务器,使用安全组限制
# 只允许应用服务器 IP 访问 Redis
4.6 安全检查清单
#!/bin/bash
# filename: redis_security_check.sh
# Redis 安全检查脚本
echo "=== Redis 安全检查 ==="
echo ""
# 1. 检查是否设置了密码
REQUIREPASS=$(redis-cli CONFIG GET requirepass | tail -1)
if [ "$REQUIREPASS" = "" ] || [ "$REQUIREPASS" = "" ]; then
echo "[WARN] Redis 未设置密码!"
else
echo "[OK] Redis 已设置密码"
fi
# 2. 检查绑定地址
BIND=$(redis-cli CONFIG GET bind | tail -1)
if [[ "$BIND" == *"0.0.0.0"* ]]; then
echo "[WARN] Redis 绑定了 0.0.0.0,可能存在安全风险!"
else
echo "[OK] Redis 绑定地址: $BIND"
fi
# 3. 检查是否禁用危险命令
DEBUG=$(redis-cli CONFIG GET rename-command | grep -c DEBUG)
if [ $DEBUG -gt 1 ]; then
echo "[WARN] DEBUG 命令未被禁用!"
else
echo "[OK] DEBUG 命令已禁用或重命名"
fi
# 4. 检查最大连接数
MAXCLIENTS=$(redis-cli CONFIG GET maxclients | tail -1)
echo "最大连接数: $MAXCLIENTS"
# 5. 检查保护模式
PROTECTED=$(redis-cli CONFIG GET protected-mode | tail -1)
if [ "$PROTECTED" = "yes" ]; then
echo "[OK] 保护模式已启用"
else
echo "[WARN] 保护模式已关闭!"
fi
# 6. 检查是否使用 root 运行
REDIS_USER=$(ps aux | grep redis-server | grep -v grep | awk '{print $1}')
if [ "$REDIS_USER" = "root" ]; then
echo "[WARN] Redis 使用 root 用户运行!"
else
echo "[OK] Redis 使用 $REDIS_USER 用户运行"
fi
echo ""
echo "=== 检查完成 ==="
5 主从复制配置
5.1 主从配置
# 从库配置(redis.conf)
replicaof 192.168.1.100 6379
# 或者临时执行
redis-cli REPLICAOF 192.168.1.100 6379
# 取消复制(成为独立主库)
redis-cli REPLICAOF NO ONE
5.2 主从配置参数
# 从库配置
replicaof 192.168.1.100 6379
replica-serve-stale-data yes # 主库失联时从库是否响应读请求
replica-read-only yes # 从库只读
repl-diskless-sync no # 是否使用无盘复制
repl-diskless-sync-delay 5 # 无盘复制延迟
repl-disable-tcp-nodelay no # 是否禁用 TCP_NODELAY
replica-priority 100 # 从库优先级,哨兵选举时使用
5.3 验证复制状态
# 查看复制信息
redis-cli INFO replication
# 输出示例
# role:master
# connected_slaves:2
# slave0:ip=192.168.1.101,port=6379,state=online,offset=12345,lag=0
# slave1:ip=192.168.1.102,port=6379,state=online,offset=12345,lag=0
# 从库视角
# role:slave
# master_host:192.168.1.100
# master_port:6379
# master_link_status:up
# 检查复制延迟
redis-cli -h <slave_ip> REPLICAOF NO ONE
# 然后比较两边的 DBSIZE
6 哨兵集群
6.1 哨兵配置
# sentinel.conf
# 哨兵端口
port 26379
# 哨兵监控的主节点
# sentinel monitor <master_name> <master_ip> <master_port> <quorum>
sentinel monitor mymaster 192.168.1.100 6379 2
# 密码(如果有)
sentinel auth-pass mymaster your_password
# 主节点down后多久进行故障转移
sentinel down-after-milliseconds mymaster 30000
# 故障转移超时时间
sentinel failover-timeout mymaster 180000
# 故障转移时同时同步的新从库数量
sentinel parallel-syncs mymaster 1
# 自动发现其他哨兵
sentinel monitor mymaster 192.168.1.100 6379 2
sentinel known-replica mymaster 192.168.1.101 6379
sentinel known-sentinel mymaster 192.168.1.200 26379
6.2 启动哨兵
# 启动哨兵
redis-sentinel /etc/redis/sentinel.conf
# 或者
redis-server /etc/redis/sentinel.conf --sentinel
# 查看哨兵状态
redis-cli -p 26379 INFO sentinel
# 查看所有监控的主节点
redis-cli -p 26379 SENTINEL masters
# 查看特定主节点的详细信息
redis-cli -p 26379 SENTINEL MASTER mymaster
# 查看主节点的所有从库
redis-cli -p 26379 SENTINEL REPLICAS mymaster
6.3 哨兵部署建议
- 至少部署 3 个哨兵实例
- 哨兵应该部署在不同的物理机器上
- 哨兵数量应该 >= (2n+1),其中 n 为主库数量
- quorum 设置应该为 (哨兵数量/2)+1
# 3 个哨兵的配置
# sentinel.conf
sentinel monitor mymaster 192.168.1.100 6379 2
# 5 个哨兵的配置
# sentinel.conf
sentinel monitor mymaster 192.168.1.100 6379 3
7 集群模式
7.1 Redis Cluster 配置
# redis.conf (集群节点配置)
cluster-enabled yes
cluster-config-file nodes.conf
cluster-node-timeout 15000
cluster-replica-validity-factor 10
cluster-migration-barrier 1
cluster-require-full-coverage yes
7.2 创建集群
# 假设有 6 个 Redis 实例
# 3 个主库: 192.168.1.101:6379, 192.168.1.102:6379, 192.168.1.103:6379
# 3 个从库: 192.168.1.104:6379, 192.168.1.105:6379, 192.168.1.106:6379
# 创建集群
redis-cli --cluster create \
192.168.1.101:6379 \
192.168.1.102:6379 \
192.168.1.103:6379 \
192.168.1.104:6379 \
192.168.1.105:6379 \
192.168.1.106:6379 \
--cluster-replicas 1
# 参数说明:
# --cluster-replicas 1: 每个主库配置 1 个从库
7.3 集群管理命令
# 查看集群状态
redis-cli -c -h 192.168.1.101 -p 6379 CLUSTER INFO
# 查看节点列表
redis-cli -c -h 192.168.1.101 -p 6379 CLUSTER NODES
# 查看槽分配
redis-cli -c -h 192.168.1.101 -p 6379 CLUSTER SLOTS
# 添加新节点
redis-cli --cluster add-node 192.168.1.107:6379 192.168.1.101:6379
# 添加从库
redis-cli --cluster add-node 192.168.1.108:6379 192.168.1.101:6379 --cluster-slave
# 删除节点
redis-cli --cluster del-node 192.168.1.101:6379 <node_id>
# 重新分片
redis-cli --cluster reshard 192.168.1.101:6379
# 均衡槽分布
redis-cli --cluster rebalance 192.168.1.101:6379
# 故障转移
# 从库执行
redis-cli -h 192.168.1.104 -p 6379 CLUSTER FAILFAILOVER
7.4 集群扩缩容
# 扩容:添加新主库
# 1. 启动新节点
redis-server /etc/redis/redis.conf --daemonize yes
# 2. 添加到集群
redis-cli --cluster add-node 192.168.1.107:6379 192.168.1.101:6379
# 3. 重新分片(分配槽)
redis-cli --cluster reshard 192.168.1.101:6379
# 按照提示输入目标节点 ID 和槽数量
# 缩容:移除主库
# 1. 将槽迁移到其他主库
redis-cli --cluster reshard 192.168.1.101:6379
# 选择一个节点接收 192.168.1.107 的槽
# 2. 删除节点
redis-cli --cluster del-node 192.168.1.101:6379 <node_id_of_192.168.1.107>
8 监控与告警
8.1 INFO 命令详解
# 查看所有信息
redis-cli INFO
# 查看特定 section
redis-cli INFO server # 服务器信息
redis-cli INFO clients # 客户端信息
redis-cli INFO memory # 内存信息
redis-cli INFO persistence # 持久化信息
redis-cli INFO stats # 统计信息
redis-cli INFO replication # 复制信息
redis-cli INFO cpu # CPU 信息
redis-cli INFO commandstats # 命令统计
redis-cli INFO latencystats # 延迟统计
redis-cli INFO sentinel # 哨兵信息
8.2 关键监控指标
# 内存使用
redis-cli INFO memory | grep -E "used_memory_human|maxmemory_human|mem_fragmentation_ratio"
# 连接数
redis-cli INFO clients
# connected_clients: 100
# blocked_clients: 0
# tracking_clients: 0
# QPS
redis-cli INFO stats | grep -E "instantaneous_ops_per_sec|total_commands_processed"
# 复制延迟
redis-cli INFO replication | grep -E "master_repl_offset|replica_offset"
# 持久化状态
redis-cli INFO persistence | grep -E "rdb_last_bgsave_status|aof_last_write_status"
8.3 慢查询日志
# 开启慢查询日志
redis-cli CONFIG SET slowlog-log-slower-than 10000 # 10ms
redis-cli CONFIG SET slowlog-max-len 128
# 查看慢查询
redis-cli SLOWLOG GET 10
# 清空慢查询
redis-cli SLOWLOG RESET
# 查看慢查询数量
redis-cli SLOWLOG LEN
slowlog 条目字段:
1) (integer) 4 # 条目 ID
2) (integer) 1609459200 # Unix 时间戳
3) (integer) 5000 # 执行时间(微秒)
4) 1) "GET" # 命令
2) "mykey" # 参数
5) "127.0.0.1:12345" # 客户端 IP
8.4 监控脚本
#!/bin/bash
# filename: redis_monitor.sh
# Redis 监控脚本
ALERT_EMAIL="ops@example.com"
REDIS_HOST="127.0.0.1"
REDIS_PORT="6379"
REDIS_PASSWORD=""
WARN_MEMORY_PCT=80
CRIT_MEMORY_PCT=90
# 获取内存使用率
MEMORY_INFO=$(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD INFO memory 2>/dev/null)
USED=$(echo "$MEMORY_INFO" | grep used_memory: | cut -d: -f2)
MAX=$(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD CONFIG GET maxmemory | tail -1)
MEMORY_PCT=$(echo "scale=2; $USED*100/$MAX" | bc)
echo "内存使用率: ${MEMORY_PCT}%"
if (( $(echo "$MEMORY_PCT > $CRIT_MEMORY_PCT" | bc -l) )); then
echo "严重: 内存使用率超过 ${CRIT_MEMORY_PCT}%" | mail -s "[CRITICAL] Redis 内存告警" $ALERT_EMAIL
elif (( $(echo "$MEMORY_PCT > $WARN_MEMORY_PCT" | bc -l) )); then
echo "警告: 内存使用率超过 ${WARN_MEMORY_PCT}%" | mail -s "[WARNING] Redis 内存告警" $ALERT_EMAIL
fi
# 检查连接数
CLIENTS=$(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD INFO clients | grep connected_clients | cut -d: -f2)
echo "连接数: $CLIENTS"
# 检查复制延迟
REPL_LAG=$(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD INFO replication | grep -E "master_repl_offset|replica0:offset" | tail -2)
echo "复制状态: $REPL_LAG"
# 检查持久化状态
PERSISTENCE=$(redis-cli -h $REDIS_HOST -p $REDIS_PORT -a $REDIS_PASSWORD INFO persistence | grep -E "rdb_last_bgsave_status|aof_last_write_status")
echo "持久化状态: $PERSISTENCE"
9 常见问题与解决方案
9.1 OOM(内存溢出)
现象:
OOM command not allowed when used memory > 'maxmemory'
排查:
# 查看内存使用
redis-cli INFO memory | grep used_memory
redis-cli CONFIG GET maxmemory
# 查看大 key
redis-cli --bigkeys
# 查看 key 统计
redis-cli --scan | head -1000 | xargs -I {} redis-cli DEBUG OBJECT ENCODING {}
解决:
- 增加 maxmemory
- 调整淘汰策略
- 清理无用数据
- 使用 Redis Cluster 分片
9.2 持久化失败
现象:
Background saving error
BGSAVE failed
排查:
# 查看最后保存状态
redis-cli INFO persistence
# 查看磁盘空间
df -h
# 查看 Redis 数据目录权限
ls -la /var/lib/redis/
解决:
- 确保磁盘空间充足
- 确保 Redis 用户有写权限
- 修改 dir 路径到有空间的分区
9.3 复制断开
现象:
Disconnected from master
Master link status: down
排查:
# 查看复制状态
redis-cli INFO replication
# 检查主库是否可达
telnet 192.168.1.100 6379
# 检查网络
ping -c 5 192.168.1.100
解决:
- 确保主库可达
- 检查防火墙设置
- 如果是主库挂了,等待故障转移或手动切换
9.4 响应延迟
现象:Redis 响应变慢
排查:
# 检查慢查询
redis-cli SLOWLOG GET 10
# 检查大 key
redis-cli --bigkeys
# 检查 CPU 使用
top -p $(pidof redis-server)
# 检查内存碎片
redis-cli INFO memory | grep mem_fragmentation_ratio
解决:
- 优化慢查询命令
- 拆分大 key
- 整理内存碎片
- 增加 Redis 实例
10 总结
10.1 生产环境检查清单
| 检查项 |
建议 |
| maxmemory |
设置为物理内存的 70-80% |
| 淘汰策略 |
缓存场景用 allkeys-lru |
| 持久化 |
建议同时启用 RDB 和 AOF |
| AOF 策略 |
使用 everysec 平衡性能和安全 |
| 密码 |
必须设置强密码 |
| 绑定地址 |
只监听内网 IP |
| 危险命令 |
重命名或禁用 |
| 备份 |
每日备份,保留 7 天 |
| 监控 |
监控内存、连接数、QPS |
| 日志 |
开启慢查询日志 |
10.2 推荐配置模板
# redis.conf 生产环境推荐配置
# 网络配置
bind 192.168.1.100
port 6379
timeout 300
tcp-keepalive 300
# 安全配置
requirepass your_strong_password
rename-command FLUSHDB ""
rename-command FLUSHALL ""
rename-command DEBUG ""
rename-command KEYS ""
# 内存配置
maxmemory 8gb
maxmemory-policy allkeys-lru
maxmemory-samples 5
# 持久化配置
save 900 1
save 300 10
save 60 10000
stop-writes-on-bgsave-error yes
rdbcompression yes
rdbchecksum yes
appendonly yes
appendfsync everysec
auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb
# 内存碎片
activedefrag yes
10.3 运维注意事项
- 不要在生产环境执行 KEYS 命令:会阻塞 Redis,使用 SCAN 替代
# 错误
redis-cli KEYS "user:*"
# 正确
redis-cli --scan --pattern "user:*" | head -1000
- 使用连接池:避免频繁创建和销毁连接
# Python 示例
import redis
pool = redis.ConnectionPool(host='127.0.0.1', port=6379, password='pass', max_connections=50)
r = redis.Redis(connection_pool=pool)
- 合理设置 TTL:避免 key 永不过期导致内存持续增长
# 设置 24 小时过期
SET session:12345 "data" EX 86400
# 或者
EXPIRE session:12345 86400
- 定期维护:定期检查内存、连接数、慢查询等指标
Redis 部署表面简单,但生产环境里的坑确实不少。希望这份指南能帮你绕开这些坑,部署出稳定可靠的 Redis 服务。更多实战经验也欢迎到云栈社区一起交流。