目录
- Redis内存回收
- 问题原由
- 遍历清除垃圾键
- 如何遍历键
- 如何判断键是否垃圾
- 获取键大小
- 管道加速
- 脚本实现
- 从根源避免问题
- 小结
Redis内存回收
Redis 服务器的最大占用内存量由配置项 maxmemory 决定,我们可以通过 config set maxmemory 2GB 的格式来配置。一旦 Redis 内存满,所有引起内存增加的操作都会被返回 error。作为专业 Redis 服务器我们通常将此项设置为0,以服务器系统内存来作为限制;
那么 Redis 使用内存达到了上限怎么办?Redis 为我们提供了几种选项以自动回收内存,可以通过配置项 maxmemory-policy 来配置;
- noeviction 不回收;
- allkeys-lru 从所有键中删除最近最少使用的键;
- volatile-lru 从设置了过期时间的键中删除最近最少使用的键;
- allkeys-random 从所有键中随机删除;
- volatile-random 从设置了过期时间的键中随机删除;
- volatile-ttl 从设置了过期时间的键中选择存活时间最短的键删除;
最大内存回收策略需要根据业务来配置,如果纯粹做缓存,allkeys-lru无疑是最合适的。如果存储了稍微重要的数据,为了防止 Redis 误删一些重要键,则需要选用 noeviction;
allkeys-lru、allkeys-random 在内存满时都有键可删,可以腾出内存,但如果配置了其他的策略,数据库用久了(根据业务量),随着业务发展和数据积累,通常会累积到到服务器内存占用率高,利用率低的情况,则可能会遇到内存占用满的问题。
问题原由
产生问题的原因有:
AG广东会,山东十一选五,大发:持久键废弃
这是导致此问题的最常见情况。
有时候是开发人员的锅,开发不规范,未给有时效性的键设置过期时间,后续又不进行手动删除,键就成为无人管的孤儿键了。
还可能是整个业务慢慢被废弃,不知道哪一天起,业务整体已不再维护了,一批键自然也就没用了。比这更严重的是,如果使用 List 传递数据,消费进程已被停止,但生产进程未同步停止,还在往 Redis 里写数据。
AG广东会,山东十一选五,大发:过期键未回收
这个原因首先要谈到 Redis 的两种过期键删除策略:
- 惰性删除:在读取键时发现键已过期,则将其删除。
- 定期删除:Redis 会从所有设置了过期时间的键中选取 100 个,删除已过期的键,如果已过期的键超过 25 个,则再次进行此操作。 此删除操作由配置项 hz 决定,Redis 默认每秒进行 10 次;
如果我们产生过期键的速度很快,最多可导致 Redis 25% 的过期键没有被及时删除。
遍历清除垃圾键
由上,明白了问题产生的原因,解决 Redis 内存满的方法就明确了:清除这些垃圾键。 于是就面临着两个问题:
AG广东会,山东十一选五,大发:如何遍历键
对于查找键,我们首先想到的是 KEYS,但 KEYS 的时间复杂度是O(n),n 是 Redis 内键的总数,如果 Redis 内键很多还是会有性能问题,导致其他命令被阻塞的。
这里介绍一个键遍历命令: SCAN。
SCAN cursor:
0 => cursor, // cursor = 0 遍历结束
1 => array(key1, key2...)
需要注意的是 SCAN 命令是在版本2.8.0 加入的,如果是之前的版本,可以考虑解析 Redis 的 RDB 文件来获取所有的键。
AG广东会,山东十一选五,大发:如何判断键是否垃圾
我们有三种异常键需要处理:
- 过期键:这些键会在被 SCAN 到时被自动删除,不再考虑。如果是解析 RDB 文件获取到的键,在查询时也会被自动删除;
- 长时间未读写的键,很可能是业务不再需要的键;
- 占用大量内存的键,有可能是在不停地写,但未消费。
这里介绍 Redis 的另一个命令 OBJECT,使用它可以从内部查看 key 对象的状态。使用 OBJECT IDLETIME key 来获取 key 的闲置时间,我们可以判断 key 闲置时间大于一个时间段(根据业务自定)的为已废弃。
此外还能使用 OBJECT REFCOUNT key获取 key 引用所储存的值的次数,OBJECT ENCODING key 获取 key 储存的值所使用的内部表示。
AG广东会,山东十一选五,大发:获取键大小
而获取 Redis 某键占用内存大。蛲ü硪桓雒 DEBUG OBJECT 来获。嗣罨岱祷乇萇BJECT命令更详细的内部数据。
DEBUG OBJECT test
Value at:0x7fb0ee16ebd0 refcount:1 encoding:embstr serializedlength:6 lru:12362780 lru_seconds_idle:4
结果包括内存地址、引用数、内部编码表示、序列化后的长度、最近最少使用标识值,闲置时间,我们可以解析此结果串来获取对应的数据。
需要注意,key 作为复合键拥有大量字段时使用 DEBUG 命令计算内存会使 Redis 阻塞较长时间,且 Redis 官方并不建议在客户端使用此命令。
我们也可以先使用 TYPE key 获取键的类型,再根据类型获取其键的大。缍宰址褂肔EN,对 哈希表使用HLEN。
要注意在删除特别大的复合键时,建议先逐步清空键内的字段,防止因字段过多,Redis 阻塞较长时间。
AG广东会,山东十一选五,大发:管道加速
Redis 支持 pipeline 管道技术,一次 请求/响应 服务器能实现处理并响应多个请求。这样就可以将多个命令同时发送到服务器,不等待回复,直接在最后获取多个结果。
PHP 中使用 MULTI(Redis::PIPELINE) 和 EXEC() 命令来实现管道;
AG广东会,山东十一选五,大发:脚本实现
下面是个简单的脚本:
$redis = new Redis();
$redis->connect('127.0.0.1');
do {
$keys = $redis->scan($cursor);
$pipeline = $redis->multi(Redis::PIPELINE);
foreach ($keys as $key) {
$idle_time = $redis->object('idletime', $key);
if ($idle_time > 180 * 24 * 3600) {
$pipeline->del($key);
}
// todo 判断类型进而判断占用内存大。偕境
}
$pipeline->exec();
} while ($cursor != 0);
从根源避免问题
以上的脚本肯定也会在删除键时影响 Redis 的效率,最好的情况还是从根源就避免此类情况,以下是一些建议:
- 规范化开发;
- 首先是键命名要规范,让人见名知义,这样在人工排错或删除时也有判断依据,然后最好有完善的 Redis 键文档,以保证业务在很长时间,经手多人后也能资料可查。
- 使用 HashSet 替代 Key-Value;
- 将业务中某一族的键以 HashSet 的方式存储,以替代普通的 key-value 类型。不仅可以省去为每个键设置前缀以节约内存,也便于统一管理。
- 有时效性的键注意设置过期时间;
- 合理设置定时清除过期键频率 hz,在 Redis 不做多余操作的情况下,使过期键尽量能被删除;
- 做好 Redis 内存的监控,在达到某个阈值时查找问题并解决。
小结
Redis假死
我在使用守护进程时 Redis 有假死情况,PHP 和 Redis 都不报错,但命令都返回 false,这种情况可以使用 Redis 的 ping() 命令,来探测 Redis 连接是否还在,如果不在则再建立新的连接。此问题很可能是由服务器配置引起的,如果您有知道此问题的原由或有好的解决办法,烦请指点一二。
危险命令
不要在没看文档的情况下在线上使用 Redis 命令,例如 debug segfault,别问我怎么知道的。
以上就是详解Redis瘦身指南的详细内容,更多关于Redis瘦身指南的资料请关注脚本之家其它相关文章!
您可能感兴趣的文章:- 基于Docker搭建Redis主从集群的实现
- redis实现共同好友的思路详解