吃透Redis(十一):Redis原子性的保证

2023-05-16

redis原子性保证

Redis server 一旦和一个客户端建立连接后,就会在事件驱动框架中注册可读事件,这就对应了客户端的命令请求。而对于整个命令处理的过程来说,我认为主要可以分成四个阶段:

  • 命令读取
  • 命令解析
  • 命令执行
  • 结果返回

这四个阶段在 Redis 6.0 版本前都是由主 IO 线程来执行完成的。虽然 Redis 使用了 IO 多路复用机制,但是该机制只是一次性获取多个就绪的 socket 描述符,对应了多个发送命令请求的客户端。而 Redis 在主 IO 线程中,还是逐一来处理每个客户端上的命令的,所以命令执行的原子性依然可以得到保证。

而当使用了 Redis 6.0 版本后,命令处理过程中的读取、解析和结果写回,就由多个 IO 线程来处理了。不过你也不用担心,多个 IO 线程只是完成解析第一个读到的命令,命令的实际执行还是由主 IO 线程处理。当多个 IO 线程在并发写回结果时,命令就已经执行完了,不存在多 IO 线程冲突的问题。所以,使用了多 IO 线程后,命令执行的原子性仍然可以得到保证。

为什么并发IO线程读写还能保证处理的原子性?

答:主线程负责把read pending队列中的数据放入到这些IO线程的io_threads_list队列,并且处理io_threads_list[0]也就是主线程处理IO操作,处理完成之后,主线程自旋等待IO线程处理完之后,才开始一个个执行命令,所以保证了原子性。
看源码:

void beforeSleep(struct aeEventLoop *eventLoop) {
    ...
    // 处理read pending队列的客户端队列
    handleClientsWithPendingReadsUsingThreads();
    ...
}

int handleClientsWithPendingReadsUsingThreads(void) {
    // 获取clients_pending_read队列列表迭代器
    listIter li;
    listNode *ln;
    listRewind(server.clients_pending_read,&li);
    int item_id = 0;
    
    // 一,放入不同的IO线程中
    // 遍历所有待读取的客户端,并将其散列到不同IO线程处理列表中
    while((ln = listNext(&li))) {
        client *c = listNodeValue(ln);
        // 通过取余方式散列获取IO线程下标
        int target_id = item_id % server.io_threads_num;
        // 将该客户端放入该下标列表中
        listAddNodeTail(io_threads_list[target_id],c);
        item_id++;
    }
    // 所有连接放入到IO线程处理列表后将IO线程操作标识为IO_THREADS_OP_READ读操作
    io_threads_op = IO_THREADS_OP_READ;
    for (int j = 1; j < server.io_threads_num; j++) {
        // 设置io_threads_pending为非零数,也即当前需要处理的客户端数量,这时线程将会响应该操作,开始处理客户端连接
        int count = listLength(io_threads_list[j]);
        io_threads_pending[j] = count;
    }
    
    // 二、处理主线程的IO读写时间
    //io_threads_list数组0下标处为main线程处理,也即main线程处理一部分读IO
    listRewind(io_threads_list[0],&li);
    while((ln = listNext(&li))) {
        client *c = listNodeValue(ln);
        readQueryFromClient(c->conn);
    }
    // 清空主线程负责的下标为0的客户端列表,其他的下标由IO线程自己处理
    listEmpty(io_threads_list[0]);
    
    // 三、自旋等待所有IO线程全部处理完
    // 自旋等嗲其他线程处理IO完毕
    while(1) {
        unsigned long pending = 0;
        for (int j = 1; j < server.io_threads_num; j++)
            pending += io_threads_pending[j];
        if (pending == 0) break;
    }
    
    // 四、执行命令
    // 当所有IO线程将clients_pending_read的客户端读IO处理完毕后,在主线程中处理客户端命令
    while(listLength(server.clients_pending_read)) {
        ln = listFirst(server.clients_pending_read);
        client *c = listNodeValue(ln);
        // 去掉CLIENT_PENDING_READ标志位,并将其从clients_pending_read队列中移除
        c->flags &= ~CLIENT_PENDING_READ;
        listDelNode(server.clients_pending_read,ln);
        // 如果设置暂停客户端请求那么继续循环
        if (clientsArePaused()) continue;
        // 处理客户端命令
        if (processPendingCommandsAndResetClient(c) == C_ERR) {
            continue;
        }
        processInputBuffer(c);
        // 如果处理完毕且有数据需要写回,那么将客户端放入clients_pending_write队列等待IO线程完成写操作
        if (!(c->flags & CLIENT_PENDING_WRITE) && clientHasPendingReplies(c))
            clientInstallWriteHandler(c);
    }
    server.stat_io_reads_processed += processed;
    return processed;
}
  • 客户端A 先发起请求1,后客户端B发起请求2,服务端【无法保证】先接收到 请求1后接收到请求2,因为网络传输时间不同。
  • 客户端A 先发起请求1,后客户端A再次发起请求2,服务端 【可以保证】 先接收到请求1后接收到请求2,这个由TCP来保证。
  • 服务端先接收到请求1,后接收到请求2,在多io环境下,redis【可以保证】先执行请求1后执行请求2。请求会先放到列表里,多IO线程从列表依次获取请求,进行命令读取及解析,待所有IO线程都处理完成之后,主线程才开始按序执行命令。

我们可以简单地用一段话来描述Redis的请求处理流程:Redis主线程一次性获取最大为1000个客户端连接,将其放入到read pending队列中,在下一次aeMain主循环中调用beforeSleep函数,该函数将read pending队列和write pending队列中的客户端散列到IO线程中执行读写操作,并且自身负责下标为0处的客户端,然后等待IO线程执行 read、write 完毕后再执行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

吃透Redis(十一):Redis原子性的保证 的相关文章

  • 深度学习算法训练和部署流程介绍--让初学者一篇文章理解算法训练和部署流程

    目录 1 什么是深度学习算法 2 算法训练 2 1 训练的原理 2 2 名词解释 3 算法C 43 43 部署 3 1 嵌入式终端板子部署 3 3 1 tpu npu推理 3 3 2 cpu推理 3 2 服务器部署 3 2 1 智能推理 3
  • 使用busybox构建根文件系统

    目录 1 下载busybox 2 修改Makefile 3 配置busybox 4 编译安装 4 1 usr include unistd h 203 error conflicting types for 39 gid t 39 4 2
  • CURL发送HTTP请求获得Json数据串保存在本地(linux 下 C++ 编程)

    某AI比赛中需要用到curl发送http请求 xff0c 然后返回一个json字符串 xff0c 看一下别人怎么写的 include lt iostream gt include lt string gt include lt curl c
  • 各种交叉编译工具链的区别

    目录 1 命名规则 2 实例 2 1 arm none eabi gcc 2 2 arm none linux gnueabi gcc 2 3 arm eabi gcc 2 4 armcc 2 5 arm none uclinuxeabi
  • 如何在Windows上搭建NFS服务器实现开发板与Windows之间的文件共享

    目录 1 安装nfs exe 2 mounting 172 31 8 183 f nfs on mnt nfs failed No such file or directory 3 mounting 172 31 8 183 d nfs o
  • STM32的"异常“、“中断”和“事件”区别和理解

    1 异常与中断 xff08 Cortex M3 xff09 1 1 异常与中断 原话 xff1a Cortex M3 在内核水平 上搭载了一个异常响应系统 xff0c 支持为数众多的系统异常和外部中断 其中 xff0c 编号为 1 xff0
  • 8、解决Linux无法上网的各种问题

    最近发现Linux重新开机后无法上网 xff0c 不仅不能ping通windows主机也不能上外网 ifconfig后eth0也没有分配IP地址 xff0c 总之各种问题都被我碰到了 现在来一一解决 xff01 1 没有分配到IP地址 开机
  • Linux的硬盘和硬盘分区方式表示方法

    通过一个例子来看Linux上的硬盘表示方式 xff1a dev sda dev表示我们所用的物理硬盘设备文件是放在 34 34 目录的 34 dev 34 目录下 再说sda之前我们应该知道这些知识 xff1a SCSI SATA USB
  • Pixhawk系统架构介绍

    前段时间导师叫我做扑翼无人机 xff0c 工程上需要实现的 xff0c 能够通过程控飞起来 xff0c 感觉难度挺大 先从研究PX4开始 xff0c 打算一步步理解透整个PX4的框架 xff0c 机型的适配 旋翼 固定翼的姿态控制 xff0
  • PX4启动脚本分析

    启动脚本是一个神奇的东西 xff0c 它能够识别出你对应的飞机类型 xff0c 加载对应的混控器 xff0c 选择对应的姿态 位置估计程序以及控制程序 xff0c 初始化你需要的驱动程序 下面来分析下 图片总结 启动代码的分析必定少不了这张
  • PX4使用WIFI模块

    PX4支持WIFI模块作为数传工具 xff0c 官网上也提供了教程 xff0c 但是不注意细节也很容易失败 以下是我的经验以及教训 配置环境 飞控 xff1a pixhawk v2 xff0c 烧录PX4 v1 8 2的固件 地面站 xff
  • RT-Thread和Freertos哪个更适合学习开发使用呢

    Freertos是一个国外推出的一个迷你的实时操作系统内核 xff0c 开源 xff0c 功能包括 xff1a 任务管理 时间管理 信号量 消息队列 内存管理 记录功能 软件定时器 协程等 xff0c 可基本满足较小系统的需要 RT Thr
  • TX2 相关指令

    1 查看L4T版本 head n 1 etc nv tegra release 版本如下图所示 xff0c 为28 2 1 官网查看对应版本 https developer nvidia com embedded jetpack 如下图所示
  • VNC server 5.3.0 服务端密码配置

    下载地址 xff1a 链接 xff1a https pan baidu com s 11P8Nb 6MSHE1LjX97HH6w pwd 61 1234 提取码 xff1a 1234 更多资源请前往博主个人网站 xff1a http www
  • eclipse svn插件 (subclipse) 安装及错误解决 (2017)

    subclipse 插件相关网址 项目原网址 xff1a http subclipse tigris org 打开提示 This project has been moved to GitHub xff0c 项目已经移动到 GitHub x
  • [Linux_音频]_0_0_使用alsa的API,设置和获得声音

    只是一个使用alsa的API 设置和获得声音的入门例子 之后的事情就参考API自己玩吧 api网址 http www alsa project org alsa doc alsa lib group simple mixer html 也可
  • centos7配置tomcat开机自启动

    centos7配置tomcat开机自启动 一些服务器命令 查看全部服务命令 xff1a systemctl list unit files type service ctrl 43 c可以回到命令输入 查看服务 xff1a systemct
  • William的Linux学习之路7

    很不好意思 xff0c 这一段时间一直在做个医院的项目 xff0c 没有坚持学习 真心感觉鸟哥的书不错 xff0c 跟着它学习没错 因为我们使用Linux xff0c 在大多情况下使用的是文本命令界面 xff0c 所以有时候很难直接看出哪个
  • php在Linux环境中安装扩展

    如果我们刚开始编译php的时候 xff0c 没有安装curl扩展 xff0c 后面需要安装curl扩展的话 xff0c 可以如下 xff1a 我们先看本地有没有安装curl的 xff0c 执行下面的命令查看 xff1a yum list i
  • WNMP(windows+nginx+mysql+php)环境搭建

    我们就把开发环境都放到我C盘里面的wnmp目录下 xff0c 其中nginx就安装在wnmp目录里面的nginx目录里面 xff0c mysql就安装在wnmp目录里面的mysql目录里面 xff0c php就安装在wnmp目录里面的php

随机推荐