详解Redis瘦身指南


Posted in Redis onMay 26, 2021

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。

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命令更详细的内部数据。

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() 命令来实现管道;

脚本实现

下面是个简单的脚本:

$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实现分布式锁的方法(lua脚本版)
May 12 Redis
详解Redis瘦身指南
May 26 Redis
详解缓存穿透击穿雪崩解决方案
May 28 Redis
SpringBoot 集成Redis 过程
Jun 02 Redis
解析Redis Cluster原理
Jun 21 Redis
浅谈Redis位图(Bitmap)及Redis二进制中的问题
Jul 15 Redis
Redis中一个String类型引发的惨案
Jul 25 Redis
Redis集群新增、删除节点以及动态增加内存的方法
Sep 04 Redis
Redis中有序集合的内部实现方式的详细介绍
Mar 16 Redis
Redis监控工具RedisInsight安装与使用
Mar 21 Redis
Redis 限流器
May 15 Redis
Redis基本数据类型List常用操作命令
Jun 01 Redis
Redis高级数据类型Hyperloglog、Bitmap的使用
May 24 #Redis
redis实现排行榜功能
May 24 #Redis
分布式锁为什么要选择Zookeeper而不是Redis?看完这篇你就明白了
May 21 #Redis
Redis 配置文件重要属性的具体使用
May 20 #Redis
浅谈redis缓存在项目中的使用
May 20 #Redis
详解Redis主从复制实践
详解redis分布式锁的这些坑
You might like
水质对咖图啡风味的影响具体有哪些
2021/03/03 冲泡冲煮
php中把美国时间转为北京时间的自定义函数分享
2014/07/28 PHP
浅析Yii2集成富文本编辑器redactor实例教程
2016/04/25 PHP
深入理解PHP JSON数组与对象
2016/07/19 PHP
Laravel重写用户登录简单示例
2016/10/08 PHP
PHP与JavaScript针对Cookie的读写、交互操作方法详解
2017/08/07 PHP
jQuery使用手册之二 DOM操作
2007/03/24 Javascript
关于捕获用户何时点击window.onbeforeunload的取消事件
2011/03/06 Javascript
Js日期选择器并自动加入到输入框中示例代码
2013/08/02 Javascript
nodeType属性返回被选节点的节点类型介绍
2013/11/22 Javascript
利用jQuary实现文字浮动提示效果示例代码
2013/12/26 Javascript
基于jquery实现的可编辑下拉框实现代码
2014/08/02 Javascript
sogou地图API用法实例教程
2014/09/11 Javascript
node.js中的http.response.removeHeader方法使用说明
2014/12/14 Javascript
JavaScript中匿名函数用法实例
2015/03/23 Javascript
JavaScript中的Math.LOG2E属性使用详解
2015/06/14 Javascript
asp.net+jquery.form实现图片异步上传的方法(附jquery.form.js下载)
2016/05/05 Javascript
JavaScript注入漏洞的原理及防范(详解)
2016/12/04 Javascript
JavaScript 中 apply 、call 的详解
2017/03/21 Javascript
vue+element 实现商城主题开发的示例代码
2020/03/26 Javascript
[02:32]DOTA2亚洲邀请赛 VG战队巡礼
2015/02/03 DOTA
django 做 migrate 时 表已存在的处理方法
2019/08/31 Python
使用 PyTorch 实现 MLP 并在 MNIST 数据集上验证方式
2020/01/08 Python
PyQt5如何将.ui文件转换为.py文件的实例代码
2020/05/26 Python
python中编写函数并调用的知识点总结
2021/01/13 Python
CSS3制作酷炫的三维相册效果
2016/07/01 HTML / CSS
HTML5+CSS设置浮动却没有动反而在中间且错行的问题
2020/05/26 HTML / CSS
女士和男士时尚鞋在线购物:Shoespie
2019/02/28 全球购物
介绍一下XMLHttpRequest对象
2012/02/12 面试题
大型公益活动策划方案
2014/08/20 职场文书
学校做一个有道德的人活动方案
2014/08/23 职场文书
向女朋友道歉的话
2015/01/20 职场文书
人生遥控器观后感
2015/06/11 职场文书
JavaScript中document.activeELement焦点元素介绍
2021/11/27 Javascript
python如何读取和存储dict()与.json格式文件
2022/06/25 Python
Win11怎么添加用户?Win11添加用户账户的方法
2022/07/15 数码科技