carlosfu
作者carlosfu·2023-07-10 11:39
软件开发工程师·快手

Redis 6.2和7渐进式逐出优化

字数 4030阅读 639评论 0赞 1

一、Redis 逐出功能简介

网上关于Redis逐出功能和算法的文章比较多,这里就不浪费篇幅介绍,本文旨在介绍Redis 6.2以后一个Redis逐出上的重大优化(渐进式逐出),顺带整理下Redis在各个版本上逐出功能上的优化。

Redis有一个maxmemory配置,每次执行命令时如果发现当前Redis使用内存超过maxmemory时,会使用相应的策略(包含不逐出也是一种策略)把key进行逐出,直到Redis使用内存小于maxmemory,这句话包含了几个重要信息,我们分别来看下。

1.每次执行命令时...直到Redis使用内存小于maxmemory

说明这个逐出检测是一个同步过程,如果逐出花费大量时间,Redis会长期不可用,甚至会触发切换。

2. 相应的策略

策略包含如下几种,可根据相应场景进行测试,默认是NO_EVICTION

MAXMEMORY_VOLATILE_LRU
MAXMEMORY_VOLATILE_LFU
MAXMEMORY_VOLATILE_TTL
MAXMEMORY_VOLATILE_RANDOM
MAXMEMORY_ALLKEYS_LRU
MAXMEMORY_ALLKEYS_LFU
MAXMEMORY_ALLKEYS_RANDOM
MAXMEMORY_NO_EVICTION

3. Redis使用内存

这个使用内存可能会产生歧义,造成不符合预期的事件发生,使用内存定义

使用内存 = used_memory - AOF缓冲区 - slave缓冲区


所以如果normal客户端缓冲区突增(例如持续执行很大的命令,例如lrange 大list),可能也会造成突发逐出。

4. 重要点总结

  • 同步逐出: 如果逐出量很大(例如百万级别),可能严重影响可用性
  • used_memory参考上图。
  • “模拟的”LRU、LFU:可以看下代码,LFU和LRU不是真正的(真正的成本太高了),是用一个队列模拟的。

二、Redis 6.2&7+ 逐出实验

1. 版本选择

  • Redis 6.0.15
  • Redis 7.0.11

2. 实验条件

  • maxmemory = 7.5 GB
  • 第一次灌入:70,000,000个key(为了越过 67108864 界限),观察内存
  • 第二次灌入:1,000,000个key,观察可用性和逐出 + 800MB string (40个20MB)

    ./redis-cli debug populate 40 user 20971520

3. 开始实验

(1) 第一次灌入:7000w个key

可以看到两个版本内存消耗几乎一致,并且在整个导入过程中未发生异常

(2) 第二次灌入:1,000,000个key + 800MB string (40个20MB),观察可用性和逐出
客户端异常redis-cli latency耗时
6.0.15持续发生min: 29, max: 34752500ms
7.0.11未发生min: 0, max: 2533051233ms

可以看到Redis 7.0.11在同等状况下,可用性表现良好,下面来看下相关原理

三、同步逐出与渐进式逐出(since 6.2+)

1. 同步逐出

文章开头已经提到:每次执行命令时如果发现当前使用内存超过maxmemory时,会使用相应的策略(包含不逐出也是一种策略)把key进行逐出,直到Redis使用内存小于maxmemory

2. 渐进式逐出

(1) evictionTimer运行时间超过eviction_time_limit_us后,会开启evict时间事件并退出逐出,这样将逐出循环异步化并保证可用性。

        //开始计时  
    elapsedStart(&evictionTimer);  

    /* Finally remove the selected key. */  
    if (bestkey) {  
        db = server.db+bestdbid;  
        robj *keyobj = createStringObject(bestkey,sdslen(bestkey));  
        delta = (long long) zmalloc_used_memory();  
        latencyStartMonitor(eviction_latency);  
        if (server.lazyfree_lazy_eviction)  
            dbAsyncDelete(db,keyobj);  
        else  
            dbSyncDelete(db,keyobj);  
        delta -= (long long) zmalloc_used_memory();  
        mem_freed += delta;  
        keys_freed++;  

        if (keys_freed % 16 == 0) {  
            if (slaves) flushSlavesOutputBuffers();  
            if (server.lazyfree_lazy_eviction) {  
                if (getMaxmemoryState(NULL,NULL,NULL,NULL) == C_OK) {  
                    break;  
                }  
            }  

            /* After some time, exit the loop early - even if memory limit  
             * hasn't been reached.  If we suddenly need to free a lot of  
             * memory, don't want to spend too much time here.  */  
            if (elapsedUs(evictionTimer) > eviction_time_limit_us) {  
                // We still need to free memory - start eviction timer proc  
                startEvictionTimeProc();  
                break;  
            }  
        }  
    }

(2) eviction_time_limit_us计算方法:

可以看到新增maxmemory_eviction_tenacity参数用来控制超时:

  • 当maxmemory_eviction_tenacity<=10,超时时间等50微秒 * maxmemory_eviction_tenacity
  • 当maxmemory_eviction_tenacity>10 and maxmemory_eviction_tenacity<100,超时时间等于500 * 1.15^(maxmemory_eviction_tenacity - 10.0),该值最大为2分钟
  • 当maxmemory_eviction_tenacity = 100,代表没有超时时间,和同步逐出一致。

(3) 用isEvictionProcRunning控制逐出时间事件

3. 风险讨论?

可以看到不同的maxmemory_eviction_tenacity值,逐出的粒度不尽相同:

  • 过低:可用性可以保证,但内存可能会持续增加(生产大于逐出),需要通过监控观测。
  • 过高:可用性可能有问题,但内存可以得到控制。

四、历届版本的重要优化

下图展示了Redis在各个版本evict的相关优化和功能(如有漏掉,欢迎指正)

1. Redis 3->Redis 4

  • Redis 4引入了LRU算法:详见http://antirez.com/news/109
  • Volatile-ttl和LFU、LRU一样使用了evict pool(一个采样的LFU、LFU、TTL池子进行排列)来实现。
  • 逐出支持异步删除:lazyfree-lazy-eviction

2. Redis 4->Redis 5

  • aof加载期间不执行evict逻辑
  • keys命令不触发evict逻辑

3. Redis 5->Redis 6

  • 记录lazyfree evcit的latency
  • evict支持tracking

4. Redis 6->Redis 6.2、7.0

  • 同步逐出改为渐进式逐出 (6.2)
  • 添加新的info:total_eviction_exceeded_time and current_eviction_exceeded_time (7.0)

五、结论

  1. Redis 7可以放心使用,内存、性能、可用性都有一定提升。
  2. maxmemory_eviction_tenacity默认是10,可以动态设置,maxmemory_eviction_tenacity=100的话可能存在bug(感谢仲肥大佬(Zhao Zhao)提示)
  • Redis 6.2需要大于6.2.8
  • Redis 7.0需要大于7.0.5

    • Fix a hang when eviction is combined with lazy-free and maxmemory-eviction-tenacity

    is set to 100 (#11237)

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

1

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广