redis 突然大量逐出导致读写请求block

现象

redis作为缓存场景使用,内存耗尽时,突然出现大量的逐出,在这个逐出的过程中阻塞正常的读写请求,导致 redis 短时间不可用;

背景

redis 中的LRU是如何实现的?

  1. 当mem_used内存已经超过maxmemory的设定,对于所有的读写请求,都会触发redis.c/freeMemoryIfNeeded(void)函数以清理超出的内存。
  2. 这个清理过程是阻塞的,直到清理出足够的内存空间。
  3. 这里的LRU或TTL策略并不是针对redis的所有key,而是以配置文件中的maxmemory-samples个key作为样本池进行抽样清理。
    maxmemory-samples在redis-3.0.0中的默认配置为5,如果增加,会提高LRU或TTL的精准度,redis作者测试的结果是当这个配置为10时已经非常接近全量LRU的精准度.

原因

逐出qps突增非常大的原因:一次需要逐出释放太多的空间会导致阻塞;具体的原因是 mem_tofree 的计算逻辑有问题;
mem_tofree 统计的是:实际已分配的内存总量 - AOF 缓冲区相关的内存;
如果这时候有rehash,会临时分配一个桶来做rehash,这部分内存未排除,所以在rehash阶段,算出来的mem_tofree 就会很大,造成一个时刻需要逐出大量的key,逐出的loop是阻塞的,这个阶段会block redis的请求;

逐出qps的计算:

freeMemoryIfNeeded(...)
    // 计算出 Redis 目前占用的内存总数,但有两个方面的内存不会计算在内:
    // 1)从服务器的输出缓冲区的内存
    // 2)AOF 缓冲区的内存
    // 3)AOF 重写缓冲区中的内存
    mem_used = zmalloc_used_memory();
    if (slaves) {
        listIter li;
        listNode *ln;

        listRewind(server.slaves,&li);
        while((ln = listNext(&li))) {
            redisClient *slave = listNodeValue(ln);
            unsigned long obuf_bytes = getClientOutputBufferMemoryUsage(slave);
            if (obuf_bytes > mem_used)
                mem_used = 0;
            else
                mem_used -= obuf_bytes;
        }
    }
    if (server.aof_state != REDIS_AOF_OFF) {
        mem_used -= sdslen(server.aof_buf);
        mem_used -= aofRewriteBufferSize();
    }
    // 计算需要释放多少字节的内存
    mem_tofree = mem_used - server.maxmemory;
    propagateExpire(db,keyobj);
    // 计算删除键所释放的内存数量
    delta = (long long) zmalloc_used_memory();
    dbDelete(db,keyobj);
    delta -= (long long) zmalloc_used_memory();
    mem_freed += delta;
    // 对淘汰键的计数器增一
    server.stat_evictedkeys++;

解决方案

github上 @Rosanta 给出的解决方案:释放内存的循环逻辑中最多执行一定次数,达到阈值了就不再逐出,到下个请求来时再释放一点空间;这个方案的好处是不会 block 整个进程,正常的业务读写请求无影响;潜在问题是可能单次写入的数据比释放的空间还大,导致总的内存是一直上升,而不是下降;

@antirez 给的方案:同样是迭代删除,但会加个标志,保证在迭代删除的逻辑下内存是逐渐下降的,而如果是上升的,还是会block住正常的请求(要控制主总的内存大小);
详见:
https://github.com/antirez/redis/pull/4583

ref

关于 redis 4.0的逐出算法优化
http://antirez.com/news/109

原文地址:https://www.cnblogs.com/me115/p/8516838.html

时间: 2024-10-13 02:00:26

redis 突然大量逐出导致读写请求block的相关文章

使用HAProxy、PHP、Redis和MySQL支撑10亿请求每周架构细节

[编者按]在公司的发展中,保证服务器的可扩展性对于扩大企业的市场需要具有重要作用,因此,这对架构师提出了一定的要求.Octivi联合创始人兼软件架构师Antoni Orfin将向你介绍一个非常简单的架构,使用HAProxy.PHP.Redis和MySQL就能支撑每周10亿请求.同时,你还能了解项目未来的横向扩展途径及常见的模式. 以下为译文: 在这篇文章中,我将展示一个非常简单的架构,使用HAProxy.PHP.Redis和MySQL支撑每周10亿请求.除此之外,我还将展示项目未来的横向扩展途径

Redis+MongoDB 最佳实践 做到读写分离 -摘自网络

方案1. (被否定) 加上Redis,做到MongoDB的读写分离,单一进程从MongoDB及时把任务同步到Redis中. 看起来很完美,但是上线后出现了各种各样的问题,列举一下: 1.Redis队列长度为多少合适? 2.同步进程根据优先级从MongoDB向Redis同步过程中,一次取多少任务合适?太大导致很多无谓的开销,太小又会频繁操作MongoDB 3.当某一个子任务处理较慢的时候,会导致MongoDB的前面优先级较高的任务没有结束,而优先级较低的确得不到处理,造成消费者空闲 最终方案: 在

hbase读写请求详细解释

2019/2/28 星期四 hbase读写请求详细解释hbase的读写过程读请求过程 1.客户端通过 ZooKeeper 以及-ROOT-表和.META.表找到目标数据所在的 RegionServer(就是 数据所在的 Region 的主机地址)2.zk返回结果给客户端3.联系 RegionServer 查询目标数据4.RegionServer 定位到目标数据所在的 Region,发出查询请求5.Region 先在 Memstore 中查找,命中则返回6.如果在 Memstore 中找不到,则在

reload()会导致304请求客户端无法Cache

最近发现我们项目中的一个用户体验上的问题,这个问题导致我们页面在翻页的速度非常慢,严重影响用户体验,当用户翻下一页时,css和js,图片等等,全是304状态,304表示去服务器上请求过了,发现没更新,所以不下载文件,但还是必须经过服务器请求经过对比分析发现window.location.reload()这个方法刷新页面,这个方法其实相当于用户按浏览器的F5,在很多浏览器下,按F5会发送no-cache请求头,这导致浏览器无法使用本机的缓存,必须去连接服务器.所以我现在改掉了这个方法,换成直接设置

Redis文件连接数不够导致listen sock总是可读CPU跑满

前几天碰到碰到一个线上redis CPU跑满的情况,基本无法处理正常请求了,刚开始以为是其他地方的问题,后来grep "Max open files" /proc/`pidof redis-server`/ -r  排查原来是启动redis的时候.ulimit -n 只有1024,从而无法接受新连接. 晚高峰时间段突发的大量请求导致redis连接数超过1024,从而listen sock 持续可读并且accept失败,从而CPU跑满,导致严重的雪崩.比如简单复现的话,ulimit -n

客户端ajax请求为实现Token验证添加headers后导致正常请求变为options跨域请求解决方法

客户端为了实现token认证,通过Jquery的ajaxSetup方法全局配置headers: 全局配置headers后会导致部分不需要token认证的请求变为options请求,导致跨域访问.报错信息如下: CORS概念 支持CORS请求的浏览器一旦发现ajax请求跨域,会对请求做一些特殊处理,对于已经实现CORS接口的服务端,接受请求,并做出回应. 有一种情况比较特殊,如果我们发送的跨域请求为"非简单请求",浏览器会在发出此请求之前首先发送一个请求类型为OPTIONS的"

redis占用高内存导致的socket连接失败

最近在搞ELK日志平台,部署filebeat收集日志时(输出到redis),出现了经典的[连接被目标机器积极拒绝]异常, 1)环境配置: 开发机,开发服务机(开发机上的virtualbox虚拟机,启动了ELK服务).测试机.测试服务机(ELK) 2)本机(开发机)使用可输出日志到开发服务机 + 测试服务机 3)测试机可输出日志到开发服务机,配置为测试服务机则提示 [2017-04-07T13:39:52+08:00 ERR Connecting error publishing events (

IOS网络第二天 - 02-异步HTTP请求block回调 解析

************** #import "HMViewController.h" #import "MBProgressHUD+MJ.h" @interface HMViewController () @property (weak, nonatomic) IBOutlet UITextField *username; @property (weak, nonatomic) IBOutlet UITextField *pwd; - (IBAction)logi

Redis big key过期导致应用超时

突然收到告警,提示redis挂了,同时大群也在说某某redis连接超时了,过了一会儿就恢复了.这时登上服务器,查看监控.首先看看qps: 可以看到qps并不高,但是中间有段时间没取到数据是怎么回事?那么继续看看redis的cpu使用率: 可以看到cpu已经饱和,这也就能解释为何断图了,因为redis是单线程,在使用cpu 100%以后,就无法处理其他的命令了,zabbix也就无法执行info命令取qps了.那么已经知道是cpu使用饱和造成的问题,那么到底是什么原因呢?那么继续查看,cpu使用高的