Redis频繁读取导致内存占用暴涨

上周鹅厂的一个朋友发现了这个问题,他很诧异,向我询问。

因为凭常识也会想到,只是读取量增加,又不涉及内容写入,怎么会导致内容暴涨呢?

从他发给我的两张截图也可以佐证,此刻key的数量并无变化,只是读取次数增多,但是内存占用多了一倍。

实际上,Redis除了数据本身会占用大量内容,还有一个缓冲区的内容也会占用内存。

Redis为每一个客户端都分配了一个输入缓冲区和输出缓冲区,输入缓冲区会把客户端的请求命令暂存起来,Redis主线程会从缓冲区中获取命令,当Redis处理完命令后会将结果写入到输出缓冲区中,通过输出缓冲区返回给客户端,如下所示

本文内容主要针对的是输出缓冲区。

Redis性能非常好,使用了Reactor的事件驱动模型,在它的内部,实现了一种高性能的事件调度器AE,redis会创建一个eventloop,然后把事件不断的注册到eventloop中,然后不断循环检测有无事件触发。eventloop会在一个wait周期获取一批活跃的fd。如果有大量读操作,就会有大量的AE_READABLE事件。然后读操作获取的数据并不是立刻就发送,而是将数据写入了Redis的输出缓冲区,并改变fd的状态为AC_WRITABLE。也就是说只有下个周期这些写事件才会被处理。当读操作过多的时候,缓冲区积累的数据也会迅速膨胀。

那么遇到这类问题该怎么去排查呢?

  1. 首先查看key的数量有没有大幅增长,排除业务的大量写入的情况。

  2. 使用 CLIENT LIST 命令,查看是什么客户端导致output增长,客户端在执行什么命令,同时去分析是否访问大key。其中较为关键的指标就是omem,指缓冲区的总字节数。

  3. 设置client-output-buffer-limit normal配制信息https://redis.io/docs/reference/clients/#output-buffer-limits。默认是不限制,但是可以根据服务器的实际配制进行限制。