gpt4 book ai didi

详解Redis瘦身指南

转载 作者:qq735679552 更新时间:2022-09-29 22:32:09 28 4
gpt4 key购买 nike

CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.

这篇CFSDN的博客文章详解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 在内存满时都有键可删,可以腾出内存,但如果配置了其他的策略,数据库用久了(根据业务量),随着业务发展和数据积累,通常会累积到到服务器内存占用率高,利用率低的情况,则可能会遇到内存占用满的问题.

问题原由

产生问题的原因有:

持久键废弃 。

这是导致此问题的最常见情况.

有时候是开发人员的锅,开发不规范,未给有时效性的键设置过期时间,后续又不进行手动删除,键就成为无人管的孤儿键了.

还可能是整个业务慢慢被废弃,不知道哪一天起,业务整体已不再维护了,一批键自然也就没用了。比这更严重的是,如果使用 list 传递数据,消费进程已被停止,但生产进程未同步停止,还在往 redis 里写数据.

过期键未回收 。

这个原因首先要谈到 redis 的两种过期键删除策略:

  • 惰性删除:在读取键时发现键已过期,则将其删除。
  • 定期删除:redis 会从所有设置了过期时间的键中选取 100 个,删除已过期的键,如果已过期的键超过 25 个,则再次进行此操作。 此删除操作由配置项 hz 决定,redis 默认每秒进行 10 次;

如果我们产生过期键的速度很快,最多可导致 redis 25% 的过期键没有被及时删除.

遍历清除垃圾键

由上,明白了问题产生的原因,解决 redis 内存满的方法就明确了:清除这些垃圾键。 于是就面临着两个问题:

如何遍历键 。

对于查找键,我们首先想到的是 keys,但 keys 的时间复杂度是o(n),n 是 redis 内键的总数,如果 redis 内键很多还是会有性能问题,导致其他命令被阻塞的.

这里介绍一个键遍历命令: scan.

?
1
2
3
4
scan cursor:
 
0 => cursor, // cursor = 0 遍历结束
1 => array(key1, key2...)

需要注意的是 scan 命令是在版本2.8.0 加入的,如果是之前的版本,可以考虑解析 redis 的 rdb 文件来获取所有的键.

如何判断键是否垃圾 。

我们有三种异常键需要处理:

  • 过期键:这些键会在被 scan 到时被自动删除,不再考虑。如果是解析 rdb 文件获取到的键,在查询时也会被自动删除;
  • 长时间未读写的键,很可能是业务不再需要的键;
  • 占用大量内存的键,有可能是在不停地写,但未消费。

这里介绍 redis 的另一个命令 object,使用它可以从内部查看 key 对象的状态。使用 object idletime key 来获取 key 的闲置时间,我们可以判断 key 闲置时间大于一个时间段(根据业务自定)的为已废弃.

此外还能使用 object refcount key获取 key 引用所储存的值的次数,object encoding key 获取 key 储存的值所使用的内部表示.

获取键大小 。

而获取 redis 某键占用内存大小,则通过另一个命令 debug object 来获取,此命令会返回比object命令更详细的内部数据.

?
1
2
debug object test
value at:0x7fb0ee16ebd0 refcount:1 encoding:embstr serializedlength:6 lru:12362780 lru_seconds_idle:4

结果包括内存地址、引用数、内部编码表示、序列化后的长度、最近最少使用标识值,闲置时间,我们可以解析此结果串来获取对应的数据.

需要注意,key 作为复合键拥有大量字段时使用 debug 命令计算内存会使 redis 阻塞较长时间,且 redis 官方并不建议在客户端使用此命令.

我们也可以先使用 type key 获取键的类型,再根据类型获取其键的大小,如对字符串使用len,对 哈希表使用hlen.

要注意在删除特别大的复合键时,建议先逐步清空键内的字段,防止因字段过多,redis 阻塞较长时间.

管道加速 。

redis 支持 pipeline 管道技术,一次 请求/响应 服务器能实现处理并响应多个请求。这样就可以将多个命令同时发送到服务器,不等待回复,直接在最后获取多个结果.

php 中使用 multi(redis::pipeline) 和 exec() 命令来实现管道; 。

脚本实现 。

下面是个简单的脚本:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
$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瘦身指南的资料请关注我其它相关文章! 。

最后此篇关于详解Redis瘦身指南的文章就讲到这里了,如果你想了解更多关于详解Redis瘦身指南的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com