Redis(十四):性能问题
创始人
2025-06-01 18:11:39
0

前言

上一篇介绍了 Redis 作为缓存服务器的问题。这节开始介绍 Redis 性能方面的问题。Redis 的性能主要会受以下几方面的影响。

阻塞式操作

客户端交互时的阻塞点

  1. 复杂度高的增删改查操作(复杂度达到 O(N) 及以上的操作)。
  2. 删除数据。删除操作的本质是要释放键值对占用的内存空间。为了更加高效地管理内存空间,在应用程序释放内存时,操作系统需要把释放掉的内存块插入一个空闲内存块的链表,以便后续进行管理和再分配。这个过程本身需要一定时间,而且会阻塞当前释放内存的应用程序,所以,如果一下子释放了大量内存,空闲内存块链表操作时间就会增加,相应地就会造成 Redis 主线程的阻塞。
  3. 清空数据库,相当于删除和释放所有的键值对。

由于使用了 IO 多路复用机制,避免了主线程一直处于等待网络连接或请求到来的状态,所以网络 IO 不是导致 Redis 阻塞的因素。但是如果网络 IO 过多,也会影响 Redis 的性能。

磁盘交互时的阻塞点

Redis 采用子进程的方式生成 RDB 快照文件和执行 AOF 日志重写操作,但是将 AOF 重写缓冲区中的数据追加到从服务器时,仍然会阻塞主进程。

主从节点交互时的阻塞点

主库在复制的过程中,创建和传输 RDB 文件都是由子进程来完成的,不会阻塞主线程。

但是,对于从库来说,它在接收了 RDB 文件后,需要使用 FLUSHDB 命令清空当前数据库,会阻塞从库(如果数据量大,需要较长的时间)。

此外,从库在清空当前数据库后,还需要把 RDB 文件加载到内存,这个过程的快慢和 RDB 文件的大小密切相关,RDB 文件越大,加载过程越慢。

切片集群实例交互时的阻塞点

当我们部署 Redis 切片集群时,每个 Redis 实例上分配的哈希槽信息需要在不同实例间进行传递,同时,当需要进行负载均衡或者有实例增删时,数据会在不同的实例间进行迁移。

如果你使用了 Redis Cluster 方案,而且同时正好迁移的是 bigkey 的话,就会造成主线程的阻塞,因为 Redis Cluster 使用了同步迁移。

异步子线程机制

Redis 主线程启动后,会使用操作系统提供的 pthread_create 函数创建 3 个子线程,分别由它们负责 AOF 日志写操作、键值对删除以及文件关闭的异步执行

主线程通过一个链表形式的任务队列和子线程进行交互。当收到操作命令时,主线程会把这个操作封装成一个任务,放入任务队列中,然后给客户端返回一个完成信息,表明任务已经完成。但实际上,这操作还没有执行,等到后台子线程从任务队列中读取任务后,才根据操作类型开始实际相关操作。

异步删除也称为惰性删除(lazy free)。删除或清空操作不会阻塞主线程,这就避免了对主线程的性能影响。

lazy-free 是 Redis 4.0 新增的功能,默认是关闭的。

开启 lazy-free 后,Redis 在释放一个 key 的内存时,首先会评估代价,如果释放内存的代价很小,那么就直接在主线程中操作了,没必要放到异步线程中执行(不同线程传递数据也会有性能消耗)。

对于不同的数据类型,lazy-free 的释放策略也不同:String(不管内存占用多大)、List(节点数量小于 64)、Set(int 编码存储)、Hash/ZSet(ziplist 编码存储,或非 ziplist 编码存储且元素数量小于 64),这些情况下的 key 在释放内存时,依旧在主线程中操作。可见,即使开启了 lazy-free,String 类型的 bigkey,在删除时依旧有阻塞主线程的风险。所以还是尽量不要在 Redis 中存储 bigkey。

CPU核的影响

如果在 CPU 多核场景下,Redis 实例被频繁调度到不同 CPU 核上运行的话,每调度一次,一些请求就会受到运行时信息、指令和数据重新加载过程的影响,这就会导致某些请求的延迟明显高于其他请求

当上下文切换发生后,Redis 主线程的运行时信息需要被重新加载到另一个 CPU 核上,而此时,另一个 CPU 核上的 L1、L2 缓存中,并没有 Redis 实例之前运行时频繁访问的指令和数据,所以,这些指令和数据都需要重新从 L3 缓存,甚至是内存中加载。这个重新加载的过程需要花费一定时间。而且,Redis 实例需要等待这个重新加载的过程完成后,才能开始处理请求,所以,这也会导致一些请求的处理时间增加。

可以通过绑定 Redis 实例和 CPU 核,可以有效降低 Redis 的处理时间。为了提升 Redis 的网络性能,有时还会把网络中断处理程序和 CPU 核绑定。

如果网络中断处理程序和 Redis 实例各自所绑的 CPU 核不在同一个 CPU Socket(CPU 处理器)上,那么,Redis 实例读取网络数据时,就需要跨 CPU Socket 访问内存,这个过程会花费较多时间。

所以,为了避免 Redis 跨 CPU Socket 访问网络数据,最好把网络中断程序和 Redis 实例绑在同一个 CPU Socket 上,这样一来,Redis 实例就可以直接从本地内存读取网络数据了。

把 Redis 实例绑到一个 CPU 逻辑核上时,就会导致子进程、后台线程和 Redis 主线程竞争 CPU 资源,一旦子进程或后台线程占用 CPU 时,主线程就会被阻塞,导致 Redis 请求延迟增加。

此时可以把一个 Redis 实例绑定到一个物理核上,这样,Redis 的主线程、子进程和后台线程可以共享使用一个物理核上的多个逻辑核。

也可以修改 Redis 的源码,把子进程和后台线程绑到不同的核上,从而避免对主线程的 CPU 资源竞争。

关键系统配置

Redis自身特性

自动删除过期key

Redis 键值对的 key 可以设置过期时间。默认情况下,每 100 毫秒会删除一些过期 key,具体的算法如下:

  1. 采样 ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 个数的 key,并将其中过期的 key 全部删除
  2. 如果超过 25% 的 key 过期了,则重复删除的过程,直到过期 key 的比例降至 25% 以下。

ACTIVE_EXPIRE_CYCLE_LOOKUPS_PER_LOOP 是 Redis 的一个参数,默认是 20,那么,一秒内基本有 200 个过期 key 会被删除。

如果每秒钟删除 200 个过期key,并不会对 Redis 造成太大影响。但是如果触发了重复删除,Redis 就会一直删除以释放内存空间。删除操作是阻塞的(Redis 4.0 后可以用异步线程机制来减少阻塞影响)。所以,一旦该条件触发,Redis 的线程就会一直执行删除,这样一来,就没办法正常服务其他的键值操作了,就会进一步引起其他键值操作的延迟增加,Redis 就会变慢。

造成这种现象的原因大概率是大量数据同时过期。

文件系统

Redis 的持久化会保存数据到磁盘,这个过程要依赖文件系统来完成,文件系统将数据写回磁盘的机制,会直接影响到 Redis 持久化的效率,从而影响到 Redis 处理请求的性能。

改善办法:采用高速的固态硬盘(SSD)作为 Redis 日志的写入设备;采用合理的持久化策略。

操作系统

Redis 是内存数据库,内存使用量大,如果没有控制好内存的使用量,或者和其他内存需求大的应用一起运行了,就可能受到 swap 的影响,而导致性能变慢。

内存 swap 是操作系统里将内存数据在内存和磁盘间来回换入和换出的机制,涉及到磁盘的读写,所以,一旦触发 swap,无论是被换入数据的进程,还是被换出数据的进程,其性能都会受到慢速磁盘读写的影响。

改善办法:增加机器的内存或者使用 Redis 集群。即纵向扩展和横向扩展。

内存大页

Transparent Huge Page, THP,Linux 内核从 2.6.38 开始支持内存大页机制,该机制支持 2MB 大小的内存页分配,而常规的内存页分配是按 4KB 的粒度来执行的。

虽然内存大页可以给 Redis 带来内存分配方面的收益,但是在写时复制时(生成 RDB 时、重写 AOF 时),需要拷贝大页,会导致性能变慢。

使用 Redis 时不推荐使用内存大页。

内存碎片

碎片清理是有代价的,操作系统需要把多份数据拷贝到新位置,把原有空间释放出来,这会带来时间开销。

为了尽可能减少碎片清理对 Redis 正常请求处理的影响,自动内存碎片清理功能在执行时,还会监控清理操作占用的 CPU 时间,而且还设置了两个参数,分别用于控制清理操作占用的 CPU 时间比例的上、下限,既保证清理工作能正常进行,又避免了降低 Redis 性能。这两个参数具体如下:

  • active-defrag-cycle-min 25: 表示自动清理过程所用 CPU 时间的比例不低于 25%,保证清理能正常开展;
  • active-defrag-cycle-max 75:表示自动清理过程所用 CPU 时间的比例不高于 75%,一旦超过,就停止清理,从而避免在清理时,大量的内存拷贝阻塞 Redis,导致响应延迟升高。

缓冲区

缓冲区主要就是用一块内存空间来暂时存放命令数据,以免出现因为数据和命令的处理速度慢于发送速度而导致的数据丢失和性能问题。但因为缓冲区的内存空间有限,如果往里面写入数据的速度持续地大于从里面读取数据的速度,就会导致缓冲区需要越来越多的内存来暂存数据。当缓冲区占用的内存超出了设定的上限阈值时,就会出现缓冲区溢出。

为了避免客户端和服务器端的请求发送和处理速度不匹配,服务器端给每个连接的客户端都设置了一个输入缓冲区和输出缓冲区。

输入缓冲区

输入缓冲区会先把客户端发送过来的命令暂存起来,Redis 主线程再从输入缓冲区中读取命令,进行处理。

Redis 的客户端输入缓冲区大小的上限阈值,在代码中就设定为了 1GB,并没有提供参数让我们调节客户端输入缓冲区的大小。

可能导致溢出的情况主要是下面两种:

  • 写入了 bigkey,比如一下子写入了多个百万级别的集合类型数据;
  • 服务器端处理请求的速度过慢;

输出缓冲区

当 Redis 主线程处理完数据后,会把结果写入到输出缓冲区,再通过输出缓冲区返回给客户。

一般来说,主线程返回给客户端的数据主要分为两种:

  • 简单且大小固定的 OK 响应(例如,执行 SET 命令)或报错信息;
  • 大小不固定的、包含具体数据的执行结果(例如,执行 HGET 命令);

因此,Redis 为每个客户端设置的输出缓冲区也分为两部分:

  • 一个大小为 16KB 的固定缓冲空间,用来暂存 OK 响应和出错信息;
  • 一个可以动态增加的缓冲空间,用来暂存大小可变的响应结果。

可能导致溢出的情况主要有:

  • 服务器端返回 bigkey 的大量结果;
  • 执行了 MONITOR 命令(MONITOR 是用来监测 Redis 执行的,其输出结果会持续占用输出缓冲区);
  • 缓冲区大小设置得不合理:和输入缓冲区不同,可以通过 client-output-buffer-limit 配置项来设置缓冲区大小的上限阈值;以及设置输出缓冲区持续写入数据的数量上限阈值,和持续写入数据的时间的上限阈值。

复制缓冲区

缓冲区的另一个主要应用场景,是在主从节点间进行数据同步时,用来暂存主节点接收的写命令和数据。

在全量复制过程中,主节点在向从节点传输 RDB 文件的同时,会继续接收客户端发送的写命令请求。这些写命令就会先保存在复制缓冲区中,等 RDB 文件传输完成后,再发送给从节点去执行。主节点上会为每个从节点都维护一个复制缓冲区,来保证主从节点间的数据同步。

复制缓冲区一旦发生溢出,主节点也会直接关闭和从节点进行复制操作的连接,导致全量复制失败。

主节点上会为每个从节点都维护一个复制缓冲区,如果集群中的从节点数非常多的话,主节点的内存开销就会非常大。所以,我们还必须得控制和主节点连接的从节点个数,不要使用大规模的主从集群。

复制积压缓冲区是一个大小有限的环形缓冲区。当主节点把复制积压缓冲区写满后,会覆盖缓冲区中的旧命令数据。如果从节点还没有同步这些旧命令数据,就会造成主从节点间重新开始执行全量复制。

最后

本文介绍了影响 Redis 性能方面相关的几个问题。

至此我的 Redis 学习笔记就全部更新完毕了。因为只是为了学习 Redis 和应付面试,并没有涉及到很多应用方面的内容,也没有处处深入到源码分析,而且内容的覆盖面也不够广,希望以后有机会可以继续学习完善吧。

最后再扯点废话,我个人的话是在准备 24 秋招,方向是后端开发,语言可能会使用 Java 或者 Go。目前已经学完了计网、操作系统、MySQL、Redis,其中操作系统和 Redis 方面已经写了不少博客了,计网和 MySQL 打算在后续也会整理成博客。然后是算法方面,虽然在力扣上刷了很多题,但是真正遇到笔试还是感觉很吃力,后续打算再系统性地查漏补缺一下。之后就主要学习语言方面了。

这两个月一直在找实习,但是感觉真的挺难的,以往有面试机会的大厂,今年不是简历挂就是笔试挂,也有很多比我优秀得多的人也被挂了,除了感叹大环境不好外也没有别的办法了,说实话还是有点难受的。但是也没有那么多一帆风顺吧,还是得整理整理心情继续学习,争取能在秋招有个好成绩!

相关内容

热门资讯

原生安卓系统怎样升级,从基础到... 你有没有发现,你的安卓手机用久了,有时候就像老牛拉车一样,慢吞吞的?别急,今天就来给你支个招,让你的...
安卓13系统怎么开发,开发者的... 你有没有听说安卓13系统已经发布了?这可是个大新闻呢!作为一个热衷于手机开发的小伙伴,你是不是也跃跃...
安卓q系统镜像下载,轻松升级体... 你有没有听说安卓Q系统已经发布了?这可是安卓家族里的一大亮点呢!今天,我就要来给你详细介绍一下安卓Q...
安卓系统色彩校正软件,打造个性... 你有没有发现,手机屏幕的色彩有时候会让人感觉不太对劲?有时候,画面看起来有点灰蒙蒙的,有时候又太艳丽...
苹果能否下个安卓系统,开启新篇... 你有没有想过,苹果的iOS系统会不会有一天突然宣布,它要拥抱安卓的大家庭呢?想象iPhone和iPa...
树莓派 装 安卓系统,轻松安装... 你有没有想过,用树莓派装上安卓系统,那会是怎样一番景象呢?想象一个迷你电脑,竟然能运行起我们日常使用...
安卓系统怎么打印小票,安卓系统... 你是不是也遇到了这样的烦恼:手机里存了好多重要的小票,但是想打印出来保存或者报销,却发现安卓系统里的...
安卓10安装系统应用,轻松上手... 你有没有发现,你的安卓手机最近是不是有点儿“慢吞吞”的?别急,别急,今天就来给你支个招——升级安卓1...
美国不提安卓系统华为,迈向自主... 华为与美国:一场关于技术、市场与政策的较量在当今这个数字化的世界里,智能手机已经成为我们生活中不可或...
安卓系统怎么打开ppt,选择文... 你有没有遇到过这种情况:手里拿着安卓手机,突然需要打开一个PPT文件,却怎么也找不到方法?别急,今天...
谷歌退回到安卓系统,探索创新未... 你知道吗?最近科技圈可是炸开了锅,谷歌竟然宣布要退回到安卓系统!这可不是一个简单的决定,背后肯定有着...
安卓系统待机耗电多少,深度解析... 你有没有发现,手机电量总是不经用?尤其是安卓系统,有时候明明没怎么用,电量就“嗖”的一下子就下去了。...
小米主题安卓原生系统,安卓原生... 亲爱的手机控们,你是否曾为手机界面单调乏味而烦恼?想要给手机换换“衣服”,让它焕然一新?那就得聊聊小...
voyov1安卓系统,探索创新... 你有没有发现,最近你的手机是不是变得越来越流畅了?没错,我要说的就是那个让手机焕发青春的Vivo V...
电脑刷安卓tv系统,轻松打造智... 你有没有想过,家里的安卓电视突然变得卡顿,反应迟钝,是不是时候给它来个“大保健”了?没错,今天就要来...
安卓系统即将要收费,未来手机应... 你知道吗?最近有个大消息在科技圈里炸开了锅,那就是安卓系统可能要开始收费了!这可不是开玩笑的,这可是...
雷凌车载安卓系统,智能出行新体... 你有没有发现,现在的汽车越来越智能了?这不,我最近就体验了一把雷凌车载安卓系统的魅力。它就像一个聪明...
怎样拍照好看安卓系统,轻松拍出... 拍照好看,安卓系统也能轻松搞定!在这个看脸的时代,拍照已经成为每个人生活中不可或缺的一部分。无论是记...
安卓车机系统音频,安卓车机系统... 你有没有发现,现在越来越多的汽车都开始搭载智能车机系统了?这不,咱们就来聊聊安卓车机系统在音频方面的...
老苹果手机安卓系统,兼容与创新... 你手里那台老苹果手机,是不是已经陪你走过了不少风风雨雨?现在,它竟然还能装上安卓系统?这可不是天方夜...