Redis内部的阻塞式操作以及应对方法

news/2024/11/29 2:39:16/

Redis之所以被广泛应用,很重要的一个原因就是它支持高性能访问,也正因为这样,我们必须要重视所有可能影响Redis性能的因素,不仅要知道具体的机制,尽可能避免异常的情况出现,还要提前准备好应对异常的方案。

所以,介绍一下影响Redis性能的5大方面的潜在因素,分别是:

  • Redis内部的阻塞式操作;
  • CPU核和NUMA架构的影响;
  • Redis关键系统配置;
  • Redis内存碎片;
  • Redis缓冲区。

这篇笔记,我们先学习Redis内部的阻塞式操作以及应对的方法。

Redis的网络IO和键值对读写是由主线程完成的。那么,如果在主线程上执行的操作消耗的时间太长,就会引起主线程阻塞,但是,Redis既有服务客户端的请求的键值对增删改查操作,也有保证可靠性的持久化操作,还有进行主从复制时的数据同步操作,等等,这么多操作,究竟哪些会引起阻塞呢?

接下来,我们分门别类地梳理下这些操作,并且找出阻塞式操作。

Redis实例有哪些阻塞点?

Redis实例在运行时,要和许多对象进行交互,这些不同的交互就会涉及不同的操作,下面我们来看看和Redis实例交互的对象,以及交互时会发生的操作。

  • 客户端:网络IO,键值对增删改查操作,数据库操作。
  • 磁盘:生产RDB快照,记录AOF日志,AOF日志重写;
  • 主从节点:主库生成、传输RDB文件,从库接收RDB文件、清空数据库、加载RDB文件;
  • 切片集群实例:向其他实例传输哈希槽信息,数据迁移。

为了帮你理解,我再画一张图来展示这4类交互对象你和具体操作之间的关系。

image

接下来,我们来逐个分析下在这些交互对象中,有哪些操作会引起阻塞。

1.和客户端交互时的阻塞点

网络IO有时候会比较慢,但是Redis使用了IO多路复用机制,避免了主线程一直处于等待网络连接或请求到来的状态,所以,网络IO不是导致Redis阻塞的因素。

键值对的增删改查操作是Redis和客户端交互的主要部分,也是Redis主线程执行的主要任务,所以,复杂度高的增删改查肯定会阻塞Reids

那么,怎么判断操作复杂度是不是高呢?这里有一个最基本的标准,就是看操作的复杂度是否为O(N)。

Redis中涉及集合的操作复杂度通常为O(N),我们要在使用时重视起来,例如集合元素全量查询操作HGETALL、SMEMBERS,以及集合的聚合统计操作,例如求交、并和差集。这些操作可以作为Redis的第一个阻塞点:集合全量查询和聚合操作

除此之外,集合自身的删除操作同样也有潜在的阻塞风险,你可能会认为,删除操作很简单,直接把数据删除就好了,为什么还会阻塞主线程呢?

其实,删除操作的本质是要释放键值对占用的内存空间,你可不要小瞧内存的释放过程,释放内存只是第一步,为了更加高效地管理内存空间,在应用程序释放内存时,操作系统需要把释放掉的内存块插入一个空闲内存块的链表,以便后续进行管理和再分配。这个过程本身需要一定时间,而且会阻塞当前释放内存的应用程序,所以,如果一下子释放了大量内存,空闲内存链表操作时间就会增加,相应地就会造成Redis主线程的阻塞。

那么,什么时候会释放大量内存空间呢?其实就是大量删除键值对数据的时候,最典型的就是删除包含了大量元素的集合,也称为bigkey删除,为了让你对bigkey的删除性能有一个直观的印象,我测试了不同元素数量的集合在进行删除操作时所消耗的时间,如下:
image

从这张表里,我们可以得出三个结论:

  1. 当元素数量从10万增加到100万时,4大集合类型的删除时间的增长幅度从5倍上升到了近20倍;
  2. 集合元素越大,删除所花费的时间就越长;
  3. 当删除有100万个元素的集合时,最大的删除时间绝对值已经达到了1.98s。Redis的响应时间一般在微秒级别,所以,一个操作达到了近2秒,不可避免地会阻塞主线程。

经过刚刚的分析,很显然,bigkey删除操作就是Redis的第二个阻塞点。删除操作对Redis实例性能的负面影响很大,而且在实际业务开发时很容易被忽略,所以一定要重视它。

既然频繁删除键值对都是潜在的阻塞点了,那么,在Redis的数据库级别操作中,清空数据库,必然也是一个潜在的阻塞风险点,因为它涉及删除和释放所有的键值对,所以,这就是Redis的第三个阻塞点,清空数据库

2. 和磁盘交互时的阻塞点

我之所以把Redis与磁盘的交互单独列为一类,主要是因为磁盘IO一般都是比较费时费力的,需要重点关注。

幸运的是,Redis开发者早已认识到磁盘IO会带来阻塞,所以把Redis进一步设计为采用子进程的方式生成RDB快照文件,以及执行AOF日志重写操作,这样一来,这两个操作由子进程负责执行,慢速的磁盘IO就不会阻塞主线程了。

但是,Redis直接记录AOF日志时,会根据不同的写回策略对数据做落盘保存,一个同步写磁盘的操作的耗时大约是1~2ms,如果有大量的写操作需要记录在AOF日志中,并同步写回的话,就会阻塞主线程了,这就得到了Redis的第四个阻塞点了:AOF日志同步写

3.主从节点交互时的阻塞点

在主从集群中,主库需要生成RDB文件,并传输给从库,主库在复制的过程中,创建和传输RDB文件都是由子进程来完成的,不会阻塞主线程,但是,对于从库来说,它在接收RDB文件后,需要使用FLUSHDB命令清空当前数据库,这就正好撞上了刚才我们分析的第三个阻塞点。

此外,从库在清空当前数据库后,还需要把RDB文件加载到内存,这个过程的快慢和RDB文件的大小密切相关,RDB文件越大,加载过程越慢,所以,加载RDB文件就成为了Redis的第五个阻塞点

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

最后,当我们部署Redis切片集群时,每个Redis实例上分配的哈希槽信息需要在不同实例间进行传递,同时,当需要进行负载均衡或者有实例增删时,数据会在不同的实例间进行迁移,不过,哈希槽的信息量不大,而数据迁移是渐进式执行的,所以,一般来说,这两类操作对Redis主线程的阻塞风险不大。

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

学习来源:极客时间 《Redis核心技术与实战》学习笔记 Day08


http://www.ppmy.cn/news/18988.html

相关文章

eBPF 入门开发实践指南一:介绍 eBPF 的基本概念、常见的开发工具

eBPF 入门开发实践指南一:介绍 eBPF 的基本概念、常见的开发工具 1. 为什么会有 eBPF 技术? 1.1. 起源1.2. 执行逻辑1.3. 架构 1.3.1. 寄存器设计1.3.2. 指令编码格式 1.4. 本节参考文章 2. 如何使用eBPF编程编写 eBPF 程序 2.1. BCC2.2. libbpf-bootstr…

线段树学习

线段树是用来维护区间信息的数据结构 洛谷P3372 区间加区间查询 #include<cstdio>typedef long long LL; const int N 100005; LL tree[N * 4], lazy[N * 4];void push_down(int rt, int l, int r) {if (lazy[rt]) {int mid l ((r - l) >> 1);tree[rt <&l…

计算机视觉OpenCv学习系列:第八部分、图像操作-4

第八部分、图像操作-4第一节、图像卷积操作1.图像卷积定义2.卷积函数3.代码练习与测试第二节、高斯模糊1.高斯模糊2.函数解释3.代码练习与测试第三节、像素重映射1.像素重映射定义2.重映射函数3.代码练习与测试学习参考第一节、图像卷积操作 1.图像卷积定义 卷积的基本原理&am…

cmake 04 使用 python 管理 cmake 工程

本文目标 使用 python 写一个管理 cmake 工程的 cli 程序 参考 Python CLI python Click 官网 Click 中文文档 python多文件打包.exe执行文件 argparse 文档 使用说明 详细说明 思路 使用 click 制作单独的命令, 比如 mcmake_inti,mcmake_built , 每一个命令都打包为…

深入剖析JVM垃圾收集器

文章目录前言1、新生代垃圾收集器1.1、Serial1.2、ParNew1.3、Parallel Scavenge2、老年代垃圾收集器2.1、Serial Old2.2、Parallel Old2.3、CMS&#xff08;Concurrent Mark Sweep&#xff09;3、全堆垃圾收集器3.1、Garbage First&#xff08;G1&#xff09;前言 参考资料&am…

开发人员必备的 15 个备忘单

随着网络编程技术的快速发展&#xff0c;我们必须学习很多新东西。有些语言和框架非常复杂&#xff0c;您可能记不住所有的语法或方法。备忘单是易于访问的笔记。当有人在过去目睹任何有帮助或有价值的事情时&#xff0c;包括我自己&#xff0c;我们都会做笔记。但是&#xff0…

java枚举类2023028

一个类的对象是有限而且固定的&#xff0c;比如季节类&#xff0c; 它只有4个对象&#xff1b;再比如行星类&#xff0c;目前只有8个对象。这种实例有限而且固定的类&#xff0c;在Java里被称为枚举类。在早期代码中&#xff0c;可能会直接使用简单的静态常量来表示枚&#xff…

C语言之程序结构和常量

2.1.1 C语言的发展及标准 C语言&#xff1a;一种通用的、面向过程的计算机程序设计语言&#xff08;第三代高级语言&#xff09;1972年&#xff0c;为了移植与开发UNIX操作系统&#xff0c;丹尼斯里奇在贝尔电话实验室设计开发了C语言为了利于C语言的全面推广&#xff0c;许多…