一、概述
1.1 背景介紹
Redis 在生產環境中承擔著緩存、會話存儲、消息隊列、分布式鎖等多種角色。隨著數據量增長和并發壓力上升,內存碎片、持久化 I/O 抖動、慢查詢堆積這三類問題會逐漸顯現,直接影響服務延遲和穩定性。Redis 8.x 在內存管理和持久化機制上做了若干改進,但核心調優思路與 7.x 一脈相承。
1.2 技術特點
單線程命令處理:網絡 I/O 多線程化(Redis 6.0+),命令執行仍為單線程,避免鎖競爭但對大 Key 操作敏感
jemalloc 內存分配:默認使用 jemalloc 5.x,相比 libc malloc 碎片率更低,但長期運行仍需關注 mem_fragmentation_ratio
多種持久化模式:RDB 快照、AOF 追加日志、混合持久化三種模式各有適用場景,選錯會導致性能或數據安全問題
1.3 適用場景
場景一:高并發緩存服務,QPS 10萬+,需要精細控制內存淘汰策略
場景二:金融/支付類業務,對數據持久化有強要求,需要 AOF everysec 或混合持久化
場景三:大規模 Key 存儲,存在 BigKey 風險,需要定期掃描和治理
1.4 環境要求
| 組件 | 版本要求 | 說明 |
|---|---|---|
| Redis | 8.0+ | 本文配置基于 Redis 8.x |
| 操作系統 | Linux Kernel 4.18+ | 需要支持 THP 控制、NUMA 綁定 |
| 內存 | 建議 16GB+ | maxmemory 設置為物理內存的 60-70% |
| 存儲 | SSD(AOF 場景) | AOF rewrite 對磁盤 I/O 壓力較大 |
二、詳細步驟
2.1 內存模型與碎片率分析
2.1.1 理解 jemalloc 與內存碎片
Redis 使用 jemalloc 作為默認內存分配器。jemalloc 將內存按 size class 分配,小對象(<= 14KB)走 small bin,大對象走 large bin。這種設計在高頻分配/釋放場景下碎片率遠低于 ptmalloc,但在 Key 大小分布極不均勻時仍會產生碎片。
通過INFO memory查看關鍵指標:
# 查看內存使用詳情 redis-cli -h 127.0.0.1 -p 6379 INFO memory | grep -E"used_memory|mem_fragmentation|allocator" # 關鍵輸出字段說明: # used_memory: Redis 實際使用的內存(字節) # used_memory_rss: 操作系統分配給 Redis 的內存(RSS) # mem_fragmentation_ratio: RSS / used_memory,正常范圍 1.0~1.5 # allocator_frag_ratio: jemalloc 內部碎片率
碎片率判斷標準:
mem_fragmentation_ratio < 1.0:內存被 swap,性能嚴重下降,需立即處理
1.0 ~ 1.5:正常范圍
> 1.5:碎片率偏高,考慮開啟主動碎片整理
2.1.2 主動碎片整理配置
Redis 4.0+ 引入activedefrag,8.x 默認關閉,需根據實際情況開啟:
# redis.conf 配置 activedefrag yes # 開啟主動碎片整理 active-defrag-ignore-bytes 100mb # 碎片超過 100MB 才觸發 active-defrag-threshold-lower 10 # 碎片率超過 10% 開始整理 active-defrag-threshold-upper 100 # 碎片率超過 100% 全力整理 active-defrag-cycle-min 1 # 最少占用 CPU 1% active-defrag-cycle-max 25 # 最多占用 CPU 25%
主動碎片整理會占用 CPU,在 CPU 敏感的場景下需要控制active-defrag-cycle-max,避免影響命令處理延遲。
2.2 maxmemory 策略選擇
2.2.1 各策略適用場景
# redis.conf maxmemory 8gb # 設置最大內存,建議為物理內存的 60-70% maxmemory-policy allkeys-lru # 淘汰策略
| 策略 | 淘汰范圍 | 適用場景 |
|---|---|---|
| noeviction | 不淘汰,寫入報錯 | 數據不可丟失的持久化存儲 |
| allkeys-lru | 所有 Key,LRU | 通用緩存,Key 無過期時間 |
| volatile-lru | 有 TTL 的 Key,LRU | 混合存儲(部分持久 + 部分緩存) |
| allkeys-lfu | 所有 Key,LFU | 熱點數據明顯,訪問頻率差異大 |
| volatile-lfu | 有 TTL 的 Key,LFU | 同上,但只淘汰有過期時間的 Key |
| allkeys-random | 隨機淘汰 | 訪問模式均勻,無熱點 |
| volatile-ttl | TTL 最短的 Key | 希望優先淘汰即將過期的數據 |
決策原則:純緩存場景用allkeys-lru;有熱點數據且訪問頻率差異明顯時用allkeys-lfu;混合存儲(既有緩存又有持久數據)用volatile-lru,但要確保持久數據不設 TTL。
2.3 持久化策略選擇
2.3.1 RDB vs AOF vs 混合持久化
RDB(快照):
# redis.conf save 900 1 # 900秒內有1個Key變更則觸發 save 300 10 # 300秒內有10個Key變更則觸發 save 60 10000 # 60秒內有10000個Key變更則觸發 rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir /var/lib/redis
RDB 通過fork()子進程生成快照,對主進程影響取決于內存大小和 COW(Copy-On-Write)開銷。內存 8GB 的實例,fork 耗時通常在 50~200ms,期間主進程會短暫阻塞。
AOF(追加日志):
# redis.conf appendonly yes appendfilename"appendonly.aof" appendfsync everysec # 每秒 fsync,丟失最多 1 秒數據 # appendfsync always # 每次寫入 fsync,最安全但性能最差 # appendfsync no # 由 OS 決定,性能最好但可能丟失較多數據 no-appendfsync-on-rewrite yes # AOF rewrite 期間不 fsync,避免 I/O 競爭 auto-aof-rewrite-percentage 100# AOF 文件增長 100% 時觸發 rewrite auto-aof-rewrite-min-size 64mb # AOF 文件至少 64MB 才觸發 rewrite
混合持久化(推薦生產使用):
# redis.conf(Redis 4.0+) aof-use-rdb-preamble yes # 開啟混合持久化 appendonly yes appendfsync everysec
混合持久化在 AOF rewrite 時將當前數據以 RDB 格式寫入 AOF 文件頭部,后續增量以 AOF 格式追加。恢復速度接近 RDB,數據安全性接近 AOF everysec,是生產環境的最優選擇。
2.3.2 持久化性能影響對比
| 模式 | 恢復速度 | 數據安全 | 寫入性能影響 | 磁盤占用 |
|---|---|---|---|---|
| RDB only | 快 | 低(分鐘級丟失) | 低(fork 有抖動) | 小 |
| AOF everysec | 慢 | 高(最多1秒) | 中 | 大 |
| AOF always | 慢 | 最高(無丟失) | 高(每次 fsync) | 大 |
| 混合持久化 | 快 | 高(最多1秒) | 中 | 中 |
2.4 SLOWLOG 配置與慢查詢分析
# redis.conf slowlog-log-slower-than 10000 # 記錄執行時間超過 10ms 的命令(單位微秒) slowlog-max-len 1000 # 最多保留 1000 條慢日志
# 查看慢日志 redis-cli SLOWLOG GET 20 # 獲取最近 20 條慢日志 redis-cli SLOWLOG LEN # 查看慢日志總數 redis-cli SLOWLOG RESET # 清空慢日志 # 慢日志輸出格式: # 1) 1) (integer) 14 # 日志 ID # 2) (integer) 1706745600 # 執行時間戳 # 3) (integer) 15234 # 執行耗時(微秒) # 4) 1) "KEYS" # 命令 # 2) "*user*" # 5) "127.0.0.1:52341" # 客戶端地址 # 6) "" # 客戶端名稱
三、示例代碼和配置
3.1 完整配置示例
3.1.1 生產環境 redis.conf
# 文件路徑:/etc/redis/redis.conf # 網絡 bind0.0.0.0 port 6379 protected-mode yes tcp-backlog 511 timeout 300 tcp-keepalive 300 # 內存管理 maxmemory 8gb maxmemory-policy allkeys-lru maxmemory-samples 10 # LRU/LFU 采樣數,越大越精準但越耗 CPU activedefrag yes active-defrag-ignore-bytes 100mb active-defrag-threshold-lower 10 active-defrag-threshold-upper 100 active-defrag-cycle-min 1 active-defrag-cycle-max 25 # 持久化(混合模式) appendonly yes appendfilename"appendonly.aof" appendfsync everysec no-appendfsync-on-rewrite yes auto-aof-rewrite-percentage 100 auto-aof-rewrite-min-size 64mb aof-use-rdb-preamble yes save 900 1 save 300 10 save 60 10000 rdbcompression yes rdbchecksum yes dbfilename dump.rdb dir /var/lib/redis # 慢日志 slowlog-log-slower-than 10000 slowlog-max-len 1000 # 延遲監控 latency-monitor-threshold 100 # 記錄超過 100ms 的延遲事件 # 線程(Redis 6.0+ I/O 多線程) io-threads 4 # I/O 線程數,建議為 CPU 核數的一半 io-threads-do-reads yes # 客戶端 maxclients 10000
3.2 大 Key 檢測與處理
3.2.1 檢測大 Key
# 方法一:redis-cli 內置掃描(推薦,不阻塞) redis-cli -h 127.0.0.1 -p 6379 --bigkeys -i 0.1 # -i 0.1 表示每次掃描后休眠 0.1 秒,降低對生產的影響 # 方法二:使用 SCAN 替代 KEYS(生產環境禁止使用 KEYS *) redis-cli -h 127.0.0.1 -p 6379 --scan --pattern"user:*"| head -100 # 方法三:RDB 離線分析(最全面,零影響) # 使用 rdb-tools 分析 dump.rdb pip install rdbtools python-lzf rdb --commandmemory /var/lib/redis/dump.rdb | sort -t','-k4 -rn | head -20
3.2.2 大 Key 處理策略
# 對于大 Hash(字段數 > 5000):拆分為多個小 Hash # 原始:HSET user:1000 field1 val1 field2 val2 ... field10000 val10000 # 拆分:按 field hash 分桶 # user0, user1, ... user99 # 對于大 List/Set(元素數 > 10000):分頁存儲或使用 Stream 替代 # 刪除大 Key:使用 UNLINK 替代 DEL(異步刪除,不阻塞主線程) redis-cli -h 127.0.0.1 UNLINK big_key_name # 批量異步刪除 redis-cli -h 127.0.0.1 --scan --pattern"temp:*"| xargs redis-cli UNLINK
3.3 Pipeline 與 Lua 腳本優化
3.3.1 Pipeline 減少網絡往返
# Python 示例:Pipeline 批量操作
importredis
r = redis.Redis(host='127.0.0.1', port=6379)
# 不使用 Pipeline:N 次網絡往返
foriinrange(1000):
r.set(f"key:{i}",f"value:{i}")
# 使用 Pipeline:1 次網絡往返
pipe = r.pipeline(transaction=False) # transaction=False 不使用 MULTI/EXEC,性能更高
foriinrange(1000):
pipe.set(f"key:{i}",f"value:{i}")
pipe.execute()
3.3.2 Lua 腳本保證原子性
-- 原子性 CAS(Compare And Swap)操作
-- 文件:cas.lua
localcurrent = redis.call('GET', KEYS[1])
ifcurrent == ARGV[1]then
redis.call('SET', KEYS[1], ARGV[2])
return1
else
return0
end
# 執行 Lua 腳本 redis-cli EVAL"$(cat cas.lua)"1 mykey old_value new_value # 生產環境推薦使用 SCRIPT LOAD + EVALSHA,避免每次傳輸腳本內容 redis-cli SCRIPT LOAD"$(cat cas.lua)" # 返回 SHA1,后續使用 EVALSHA sha1 1 mykey old_value new_value
四、最佳實踐和注意事項
4.1 最佳實踐
4.1.1 性能優化
禁用 THP(透明大頁):THP 會導致 fork 時 COW 開銷劇增,Redis 啟動時會警告
# 臨時禁用 echonever > /sys/kernel/mm/transparent_hugepage/enabled echonever > /sys/kernel/mm/transparent_hugepage/defrag # 永久禁用(寫入 /etc/rc.local 或 systemd service) cat >> /etc/rc.local <'EOF' echo?never > /sys/kernel/mm/transparent_hugepage/enabled echonever > /sys/kernel/mm/transparent_hugepage/defrag EOF
調整 vm.overcommit_memory:避免 fork 時因內存不足失敗
sysctl vm.overcommit_memory=1 echo"vm.overcommit_memory = 1">> /etc/sysctl.conf
連接池配置:避免頻繁建立 TCP 連接
# Python redis-py 連接池 pool = redis.ConnectionPool( host='127.0.0.1', port=6379, max_connections=50, # 最大連接數 socket_timeout=1.0, # 命令超時 1 秒 socket_connect_timeout=1.0 ) r = redis.Redis(connection_pool=pool)
4.1.2 安全加固
啟用 ACL 訪問控制(Redis 6.0+):
# redis.conf aclfile /etc/redis/users.acl # users.acl 內容 user default off # 禁用默認用戶 user appuser on >StrongPassword123 ~app:* +@read+@write +DEL # 應用用戶 user monitor on >MonitorPass ~* +INFO +MONITOR # 監控用戶
禁用危險命令:
# redis.conf rename-command KEYS "" # 禁用 KEYS rename-command FLUSHALL"FLUSHALL_DISABLED_9x8k2" rename-command FLUSHDB "FLUSHDB_DISABLED_7m3n1" rename-command CONFIG "CONFIG_9a2b3c" rename-command DEBUG ""
4.1.3 高可用配置
Sentinel 最小配置(3節點):
# sentinel.conf sentinel monitor mymaster 192.168.1.10 6379 2 # 需要 2 個 sentinel 同意才切換 sentinel down-after-milliseconds mymaster 5000 # 5秒無響應判定下線 sentinel failover-timeout mymaster 60000 # 故障轉移超時 60 秒 sentinel parallel-syncs mymaster 1 # 同時只有 1 個 slave 同步新 master
4.2 注意事項
4.2.1 配置注意事項
警告:appendfsync always在高寫入場景下會導致 Redis 吞吐量下降 90% 以上,除非業務對數據零丟失有強制要求,否則不要使用。
KEYS *命令在生產環境會阻塞 Redis 數秒,100萬 Key 的實例執行 KEYS 約需 300ms,必須用 SCAN 替代
maxmemory不設置時 Redis 會無限使用內存直到 OOM,容器環境尤其危險
AOF rewrite 期間磁盤寫入量是平時的 2-3 倍,需要預留足夠磁盤空間
4.2.2 常見錯誤
| 錯誤現象 | 原因分析 | 解決方案 |
|---|---|---|
| MISCONF Redis is configured to save RDB snapshots | 磁盤滿或權限問題導致 RDB 保存失敗 | 檢查磁盤空間和 dir 目錄權限 |
| OOM command not allowed when used memory > maxmemory | 內存達到上限且淘汰策略為 noeviction | 調整 maxmemory 或更換淘汰策略 |
| LOADING Redis is loading the dataset in memory | 重啟后正在加載 AOF/RDB | 等待加載完成,大文件可能需要數分鐘 |
| 延遲突增(latency spike) | fork 觸發 COW,或 AOF rewrite | 檢查INFO stats中latest_fork_usec,考慮減少 save 頻率 |
| 內存碎片率 > 2.0 | 大量 Key 過期后內存未釋放 | 開啟 activedefrag 或重啟實例 |
五、故障排查和監控
5.1 故障排查
5.1.1 日志查看
# 查看 Redis 日志 tail -f /var/log/redis/redis-server.log # 查看延遲事件 redis-cli -h 127.0.0.1 LATENCY HISTORY event redis-cli -h 127.0.0.1 LATENCY LATEST redis-cli -h 127.0.0.1 LATENCY RESET # 實時監控命令流(慎用,高并發下會產生大量輸出) redis-cli -h 127.0.0.1 MONITOR | head -100
5.1.2 常見問題排查
問題一:Redis 響應延遲突增
# 檢查是否有慢查詢 redis-cli SLOWLOG GET 10 # 檢查 fork 耗時 redis-cli INFO stats | grep latest_fork_usec # 檢查是否有阻塞命令 redis-cli INFO clients | grep blocked_clients # 檢查內存使用 redis-cli INFO memory | grep -E"used_memory_human|mem_fragmentation_ratio"
問題二:內存持續增長不釋放
# 檢查 Key 數量和過期情況 redis-cli INFO keyspace # 檢查內存分配詳情 redis-cli MEMORY DOCTOR # 分析內存使用 redis-cli MEMORY USAGE keyname # 查看單個 Key 內存占用
問題三:AOF 文件異常增大
# 手動觸發 AOF rewrite redis-cli BGREWRITEAOF # 檢查 rewrite 狀態 redis-cli INFO persistence | grep aof_rewrite
5.2 性能監控
5.2.1 關鍵指標監控
# 綜合狀態檢查腳本 #!/bin/bash HOST="127.0.0.1" PORT="6379" echo"=== Redis 性能快照 ===" redis-cli -h$HOST-p$PORTINFO all | grep -E "redis_version|uptime_in_days|connected_clients|blocked_clients| used_memory_human|mem_fragmentation_ratio|total_commands_processed| instantaneous_ops_per_sec|rejected_connections|keyspace_hits| keyspace_misses|expired_keys|evicted_keys|latest_fork_usec| aof_enabled|rdb_last_bgsave_status"
5.2.2 監控指標說明
| 指標名稱 | 正常范圍 | 告警閾值 | 說明 |
|---|---|---|---|
| mem_fragmentation_ratio | 1.0~1.5 | > 2.0 | 內存碎片率 |
| blocked_clients | 0 | > 10 | 被阻塞的客戶端數 |
| keyspace_hit_rate | > 90% | < 80% | 緩存命中率 |
| latest_fork_usec | < 200ms | > 1000ms | 最近一次 fork 耗時 |
| rejected_connections | 0 | > 0 | 被拒絕的連接數(達到 maxclients) |
| evicted_keys | 視業務 | 持續增長 | 被淘汰的 Key 數,持續增長說明內存不足 |
5.2.3 Prometheus 監控規則
# redis_alerts.yml
groups:
-name:redis
rules:
-alert:RedisMemoryFragmentation
expr:redis_mem_fragmentation_ratio>2.0
for:5m
labels:
severity:warning
annotations:
summary:"Redis 內存碎片率過高:{{ $value }}"
-alert:RedisHighMemoryUsage
expr:redis_memory_used_bytes/redis_memory_max_bytes>0.9
for:2m
labels:
severity:critical
annotations:
summary:"Redis 內存使用率超過 90%"
-alert:RedisCacheHitRateLow
expr:|
rate(redis_keyspace_hits_total[5m]) /
(rate(redis_keyspace_hits_total[5m]) + rate(redis_keyspace_misses_total[5m])) < 0.8
? ? ? ??for:10m
? ? ? ??labels:
? ? ? ? ??severity:warning
? ? ? ??annotations:
? ? ? ? ??summary:"Redis 緩存命中率低于 80%:?{{ $value | humanizePercentage }}"
5.3 備份與恢復
5.3.1 備份腳本
#!/bin/bash
# 文件名:redis_backup.sh
# 功能:Redis RDB 備份,保留 7 天
REDIS_CLI="/usr/bin/redis-cli"
BACKUP_DIR="/data/backup/redis"
DATE=$(date +%Y%m%d_%H%M%S)
RETENTION_DAYS=7
mkdir -p"$BACKUP_DIR"
# 觸發 BGSAVE
$REDIS_CLI-h 127.0.0.1 -p 6379 BGSAVE
# 等待 BGSAVE 完成
while["$($REDIS_CLI -h 127.0.0.1 INFO persistence | grep rdb_bgsave_in_progress | awk -F: '{print $2}' | tr -d '
')"="1"];do
sleep 1
done
# 復制 RDB 文件
cp /var/lib/redis/dump.rdb"$BACKUP_DIR/dump_${DATE}.rdb"
# 壓縮
gzip"$BACKUP_DIR/dump_${DATE}.rdb"
# 清理舊備份
find"$BACKUP_DIR"-name"dump_*.rdb.gz"-mtime +$RETENTION_DAYS-delete
echo"備份完成:$BACKUP_DIR/dump_${DATE}.rdb.gz"
5.3.2 恢復流程
停止服務:systemctl stop redis
恢復數據:cp dump_20260101_120000.rdb.gz /var/lib/redis/ && gunzip dump_*.rdb.gz && mv dump_*.rdb dump.rdb
驗證完整性:redis-check-rdb /var/lib/redis/dump.rdb
重啟服務:systemctl start redis && redis-cli PING
六、總結
6.1 技術要點回顧
內存碎片:mem_fragmentation_ratio > 1.5時開啟activedefrag,禁用 THP 是前提
淘汰策略:純緩存用allkeys-lru,熱點明顯用allkeys-lfu,混合存儲用volatile-lru
持久化:生產環境首選混合持久化(aof-use-rdb-preamble yes),兼顧恢復速度和數據安全
慢查詢:KEYS *是最常見的慢查詢來源,用SCAN替代;大 Key 刪除用UNLINK
6.2 進階學習方向
Redis Cluster 分片:水平擴展方案,16384 個 slot 的分配策略和 resharding 操作
實踐建議:先在測試環境模擬節點故障,理解 cluster-node-timeout 對業務的影響
Redis Stream:替代 List 實現消息隊列,支持消費者組和消息確認
實踐建議:對比 Kafka 的適用場景,Stream 適合輕量級、低延遲的消息場景
Redis Function(Redis 7.0+):替代 Lua 腳本的新方案,支持庫管理和持久化
實踐建議:評估是否值得從 EVALSHA 遷移到 Function
6.3 參考資料
Redis 官方文檔- 配置參數權威參考
Redis 內存優化指南- 官方內存優化建議
redis-rdb-tools- RDB 離線分析工具
附錄
A. 命令速查表
redis-cli INFO memory # 內存使用詳情 redis-cli INFO stats # 統計信息(命中率、連接數等) redis-cli INFO persistence # 持久化狀態 redis-cli SLOWLOG GET 20 # 最近 20 條慢日志 redis-cli LATENCY LATEST # 最新延遲事件 redis-cli MEMORY DOCTOR # 內存診斷建議 redis-cli --bigkeys -i 0.1 # 掃描大 Key(帶限速) redis-cli --scan --pattern"key:*"# 安全掃描 Key redis-cli BGSAVE # 觸發后臺 RDB 保存 redis-cli BGREWRITEAOF # 觸發 AOF rewrite redis-cli DEBUG SLEEP 0 # 測試延遲基線 redis-cli CLIENT LIST # 查看所有客戶端連接
B. 配置參數詳解
| 參數 | 默認值 | 推薦值 | 說明 |
|---|---|---|---|
| maxmemory-samples | 5 | 10 | LRU/LFU 采樣精度,越大越準但越耗 CPU |
| hz | 10 | 15~20 | 后臺任務頻率,影響過期 Key 清理速度 |
| dynamic-hz | yes | yes | 根據客戶端數量動態調整 hz |
| lazyfree-lazy-eviction | no | yes | 異步淘汰 Key,避免阻塞 |
| lazyfree-lazy-expire | no | yes | 異步刪除過期 Key |
| lazyfree-lazy-server-del | no | yes | DEL 命令異步執行 |
C. 術語表
| 術語 | 英文 | 解釋 |
|---|---|---|
| 內存碎片率 | mem_fragmentation_ratio | RSS 與實際使用內存的比值,反映內存利用效率 |
| 寫時復制 | Copy-On-Write (COW) | fork 后父子進程共享內存頁,修改時才復制,影響 RDB/AOF rewrite 性能 |
| 淘汰策略 | Eviction Policy | 內存達到 maxmemory 時決定刪除哪些 Key 的算法 |
| 慢日志 | Slow Log | 記錄執行時間超過閾值的命令,用于性能分析 |
| 管道 | Pipeline | 批量發送命令減少網絡往返次數的技術 |
-
命令
+關注
關注
5文章
755瀏覽量
23746 -
內存管理
+關注
關注
0文章
171瀏覽量
14878 -
Redis
+關注
關注
0文章
392瀏覽量
12185
原文標題:Redis性能優化:內存管理、持久化策略與慢查詢排查
文章出處:【微信號:magedu-Linux,微信公眾號:馬哥Linux運維】歡迎添加關注!文章轉載請注明出處。
發布評論請先 登錄
Redis內存管理、持久化策略與慢查詢排查分析
評論