元素码农
基础
UML建模
数据结构
算法
设计模式
网络
TCP/IP协议
HTTPS安全机制
WebSocket实时通信
数据库
sqlite
postgresql
clickhouse
后端
rust
go
java
php
mysql
redis
mongodb
etcd
nats
zincsearch
前端
浏览器
javascript
typescript
vue3
react
游戏
unity
unreal
C++
C#
Lua
App
android
ios
flutter
react-native
安全
Web安全
测试
软件测试
自动化测试 - Playwright
人工智能
Python
langChain
langGraph
运维
linux
docker
工具
git
svn
🌞
🌙
目录
▶
Redis核心
▶
数据结构
字符串实现
哈希表实现
列表实现
集合实现
有序集合实现
▶
内存管理
内存分配策略
淘汰算法
▶
持久化
▶
RDB机制
快照生成原理
文件格式解析
▶
AOF机制
命令追加策略
重写过程分析
▶
高可用
▶
主从复制
SYNC原理
增量复制
▶
哨兵机制
故障检测
领导选举
▶
高级特性
▶
事务系统
ACID实现
WATCH原理
▶
Lua脚本
沙盒环境
执行管道
▶
实战问题
▶
缓存问题
缓存雪崩
缓存穿透
缓存击穿
缓存预热
▶
数据一致性
读写一致性
双写一致性
▶
性能优化
大key处理
热点key优化
发布时间:
2025-03-22 10:38
↑
☰
# Redis内存淘汰策略 ## 引言 Redis作为内存数据库,当内存使用达到最大限制时,需要通过淘汰策略来释放内存空间。本文将深入探讨Redis的内存淘汰机制,包括淘汰策略的选择、实现原理以及实际应用场景。 ## 淘汰策略概述 ### 可配置的淘汰策略 Redis提供了多种淘汰策略,可以通过配置选择: ```conf # 设置最大内存 maxmemory 2gb # 设置淘汰策略 maxmemory-policy allkeys-lru ``` ### 策略类型 1. **不淘汰** - noeviction:不淘汰任何数据,内存满时返回错误 2. **淘汰所有键** - allkeys-lru:淘汰最近最少使用的键 - allkeys-random:随机淘汰键 - allkeys-lfu:淘汰最少使用频率的键 3. **淘汰设置了过期时间的键** - volatile-lru:淘汰设置了过期时间的键中最近最少使用的键 - volatile-random:随机淘汰设置了过期时间的键 - volatile-ttl:优先淘汰更早过期的键 - volatile-lfu:淘汰设置了过期时间的键中使用频率最少的键 ## 实现原理 ### 1. LRU算法实现 ```c // Redis对象结构 typedef struct redisObject { unsigned type:4; // 类型 unsigned encoding:4; // 编码 unsigned lru:24; // LRU时间(秒) int refcount; // 引用计数 void *ptr; // 指向实际值的指针 } robj; // 更新LRU时间 void touchWatchedKey(redisDb *db, robj *key) { if (server.maxmemory_policy & MAXMEMORY_FLAG_LRU) key->lru = LRU_CLOCK(); } ``` ### 2. LFU算法实现 ```c // LFU计数器结构(24位) typedef struct { unsigned long counter:16; // 访问次数 unsigned long ldt:8; // 上次递减时间 } LFUCounter; // 更新LFU计数 uint8_t LFULogIncr(uint8_t counter) { if (counter == 255) return 255; double r = (double)rand()/RAND_MAX; double baseval = counter - LFU_INIT_VAL; if (baseval < 0) baseval = 0; double p = 1.0/(baseval*server.lfu_log_factor+1); if (r < p) counter++; return counter; } ``` ### 3. 淘汰过程 ```c // 淘汰键值对 int freeMemoryIfNeeded(void) { size_t mem_used, mem_tofree; int slaves = listLength(server.slaves); // 计算需要释放的内存 mem_used = zmalloc_used_memory(); if (mem_used <= server.maxmemory) return REDIS_OK; mem_tofree = mem_used - server.maxmemory; // 根据策略淘汰数据 while (mem_tofree > 0) { int bestkey_id; sds bestkey = NULL; dictEntry *de; redisDb *db; if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_LRU || server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM) { // 从所有键中选择 db = server.db+dbid; if (dictSize(db->dict) == 0) continue; // 根据策略选择键 if (server.maxmemory_policy == REDIS_MAXMEMORY_ALLKEYS_RANDOM) de = dictGetRandomKey(db->dict); else de = dictGetSomeKeys(db->dict); bestkey = dictGetKey(de); } // 删除选中的键 dbDelete(db,bestkey); mem_tofree -= zmalloc_used_memory() - mem_used; mem_used = zmalloc_used_memory(); } return REDIS_OK; } ``` ## 策略选择建议 ### 1. 通用场景 - **allkeys-lru**:适用于大多数场景,特别是缓存场景 - **allkeys-lfu**:适用于访问频率差异明显的场景 ```conf # 通用缓存场景 maxmemory-policy allkeys-lru # 访问频率差异大的场景 maxmemory-policy allkeys-lfu ``` ### 2. 特殊场景 - **volatile-ttl**:适用于需要按过期时间淘汰的场景 - **noeviction**:适用于要求数据可靠性的场景 ```conf # 按过期时间淘汰 maxmemory-policy volatile-ttl # 不允许淘汰 maxmemory-policy noeviction ``` ## 性能优化 ### 1. 采样优化 ```c // 配置LRU采样参数 int getLRUMaxSamples(void) { return server.maxmemory_samples; } // 配置示例 config set maxmemory-samples 10 ``` ### 2. LFU参数调优 ```conf # 配置LFU参数 lfu-log-factor 10 # LFU计数器对数因子 lfu-decay-time 1 # LFU计数器衰减时间(分钟) ``` ### 3. 内存配置优化 ```conf # 设置合理的最大内存 maxmemory 2gb # 设置内存警告阈值 maxmemory-warning-limit 80% ``` ## 监控和调优 ### 1. 内存使用监控 ```redis # 查看内存使用情况 INFO memory # 查看淘汰统计 INFO stats | grep evicted ``` ### 2. 键空间分析 ```redis # 分析键的分布 DBSIZE INFO keyspace # 扫描大键 redis-cli --bigkeys ``` ### 3. 性能指标 ```redis # 监控淘汰次数 INFO stats | grep evicted_keys # 监控内存碎片率 INFO memory | grep mem_fragmentation_ratio ``` ## 最佳实践 1. **策略选择** - 优先考虑allkeys-lru或allkeys-lfu - 根据业务场景选择合适的策略 - 避免使用noeviction,除非必要 2. **内存配置** - 预留足够的系统内存 - 考虑内存碎片率 - 设置合理的警告阈值 3. **参数调优** - 根据实际情况调整采样参数 - 监控并调整LFU参数 - 定期检查内存使用情况 4. **业务优化** - 合理设置过期时间 - 避免存储过大的键值对 - 使用合适的数据结构 ## 总结 Redis的内存淘汰机制是一个复杂而精密的系统,通过合理配置淘汰策略和参数,可以在内存受限的情况下实现最优的数据淘汰效果。在实际应用中,需要根据业务场景选择合适的淘汰策略,并通过持续监控和调优来保证Redis的稳定运行。