Redis单线程 VS 多线程

一、Redis 为什么选择单线程?

这种说法其实并不严谨,为什么这么说呢?

Redis的版本有很多 3.x4.x6.x,版本不同架构也不同的,不限定版本问是否单线程也是不太严谨。

版本3.x,最早版本,也就是大家口口相传的redis的单线程,在3.x的版本是redis确实是单线程。

版本4.x,严格意义上来说也不是单线程,而是负责处理客户端请求的线程是单线程,但是开始加了多线程的东西(异步删除)。--并没有完全显现。

2020年5月版本的6.0x后及2022年推出的7.0版本后,告别了大家印象中的单线程,用一种全新的多线程来解决问题。---已经完全体现了。

里程碑的重要版本

5.0版本是直接升级到6.0版本,对于这次激进的升级,redis之父antirez表示得很有信心和兴奋,所以第一时间发文阐述了6.0的一些重大功能"Redis 6.0.0 GA is out!" ,当然,Redis7.0后版本更加厉害

1.Redis单线程介绍

        主要是指Redis网络IO的键值对读写是由一个线程来完成的,Redis在处理客户端的请求时包括获取(socket 读)、解析执行内容返回(socket 写)等都由一个顺序串行的主线程处理,这就是所谓的“单线程”。这也是 Redis对外提供键值存储服务的主要流程。

但是Redis的其他功能,比如持久化RDB、AOF、异步删除、集群数据同步等,其实是由额外的线程执行

Redis命令工作线程是单线程的,但是,整个Redis来说是多线程的。

2.单线程为什么很快?

Redis 3.x单线程时代性能依旧很快的主要原因如下:

  1. 基于内存操作Redis的所有数据都存在内存中,因此所有的运算都是内存级别的,所以他的性能比较高。
  2. 数据结构简单Redis的数据结构是专门设计的,而这些简单的数据结构的查找和操作的时间大部分复杂度都是O(1),因此性能比较高。
  3. 多路复用和非阻塞I/ORedis使用epollI/O多路复用功能来监听多个socket连接客户端,这样就可以使用一个线程来连接处理多个请求,减少线程切换带来的开销,同时也避免了I/O阻塞操作。
  4. 避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的消耗,而且单线程不会导致死锁问题的发生。

官方说明

https://redis.io/docs/get-started/faq/

Redis是单线程的,如何利用多个CPU/内核?

CPU并不是使用Redis的瓶颈,因为通常Redis要么受内存限制,例如,使用在平均Linux系统上运行的流水线Redis每秒可以发送一百万个请求,因此,如果应用主要使用 O(N) 或者 (log (N) ) 命令,则几乎不会使用过多的CPU。

但是,为了最大成都利用CPU,可以在同一框中启动多个Redis实例,并将它们视为不同的服务器,在某个时候,单核可能还是不够,因此,如果需要使用多个CPU,则可以开始考虑更早地进行分片的某种方法。

但是,在Redis 4.0中,Redis具有更多线程,目前,这个仅限于后台删除对象,以及阻止通过Redis模块实现的命令,对于将来的版本,计划使用Redis越来越线程化。

        大致意思就是说Redis是基于内存操作的,因此它的瓶颈可能是机器的内存或者网络带宽而不是CPU,既然CPU不是瓶颈,那么自然就采用单线程的解决方案了,况且使用多线程比较麻烦。但是在 Redis 4.0 中开始支持多线程了,例如后台删除、备份等功能

Redis 4.0 之前一直采用单线程的主要原因有以下三个 :

  1. 使用单线程模型是Redis的开发和维护更加简单,因为单线程模型方便开发和调试。
  2. 即使使用单线程模型也可以并发的处理多客户端的请求,主要使用的是IO多路复用和非阻塞IO;
  3. 对于Redis系统来说,主要的性能瓶颈是内存或者网络带宽而非CPU

3.Redis 为什么要加入多线程?

既然单线程这么好,为什么逐渐又要加入了多线程特性呢?

因为的单线程也存在一定的问题,如在执行复杂任务时,线程卡死等操作...

3.1 单线程的问题

        正常情况下使用del指令可以很快的删除数据,而当被删除的key是一个非常大的对象的时候,例如包含了成千上万的元素 hash 集合时,那么del指令就会造成 Redis主线程卡顿等问题。

        这就是 redis3.x单线程时代最经典的故障,大key删除的头疼的问题

        由于Redis是单线程的,del bigKey ...等待很久这个线程才会释放,类似加了一个synchronized锁,你可以详细高并发下,线程堵塞导致系统崩溃无法使用。

3.2 解决方法

        使用惰性删除可以有效的避免Redis卡顿的问题,因为在使用del key是同步操作,则可以使用异步的操作解决这个问题。

        比如当需要删除一个很大的数据时,因为是单线程原子命令操作,这就会导致Redis服务卡顿,于是在Redis4.0 中新增了多线程的模块,当然次版本中多线程主要是为了解决删除数据效率比较低的问。

unlink key

flushdb async

flushall async

包删除的工作交给后台的子线程来完成异步删除数据。

        因为Redis是单主线程处理,redis支付antirez一直强调"Lazy Redis is better Redis"。

        而lazy free的本质就是把某些cost(主要时间复制度,占用主线程cpu)较高删除操作,而redis主线程剥离让bio子线程来处理,极大地减少主线程阻塞时间。从而减少删除导致性能和稳定性问题。

        在Redis 4.0就引入了多线程来实现数据的异步惰性删除等功能,但是其实读写请求仍然只有一个线程,所以仍然算是狭义上的单线程。

二、Redis6/7的多线程特性和IO多路复用

        对于Redis主要的性能瓶颈是内存或者网络带宽而非CPU

1.网络瓶颈

在Redis6/7中,非常收关注的第一个新特性就是多线程

        这是因为,Redis一直被大家熟知的就是它的单线程架构,虽然有些命令操作可以用后台线程或者子线程执行(比如数据删除、快照生成、AOF重写)。但是,从网络IO处理到实际的读写命令处理,都是由单个线程完成的。

        随着网络硬件的性能提升,Redis的性能瓶颈有时会出现在网络IO的处理上,也就是说,单个主线程处理网络请求的速度跟不上底层网络硬件的速度,为了应对这个问题:

        采用多个IO线程来处理网络请求,提高网络请求处理的并行度,Redis6/7就是采用的这种方法。

        但是,Redis的多IO线程只是用来处理网络请求的,对于读写操作命令Redis仍然使用单线程来处理。这是因为,Redis处理请求时,网络处理经常出现瓶颈,通过多个IO线程并行处理网络操作,可以提升实例的整体处理性能。而继续使用单线程执行命令操作,就不用为了保证Lua脚本事务的原子性、额外开发多线程互斥加锁机制了(不管加锁操作处理),这样一来,Redis线程模型实现就简单了。

2.主线程和IO线程协作

阶段一:服务端和客户端建立Socket连接,并分配处理线程

        首先,主线程负责接收建立连接请求,当有客户端请求和实例建立Socket连接时,主线程会创建和客户端的连接,并把Socket放入到全局等待队列中,紧接着,主线程通过轮询方式把Socket连接分配给IO线程。注意:主线程不一定会将连接直接分配给IO线程,而是通过 epoll或者其他I/O多路复用机制,主线程会监听多个连接,一旦有事件发生,比如有数据可读,主线程会通知对应的IO线程去处理,此外Redis可能会用线程来处理这些IO任务,而不是一对一地将连接分配给IO线程。

阶段二:IO线程读取并解析请求

        主线程一旦把Socket分配给IO线程,就会进入阻塞状态,等待IO线程完成客户端请求读取解析。因为有多个IO线程在并行处理,所以,这个过程很快就可以完成。

阶段三:主线程执行请求操作

        等待IO线程解析完请求,主线程还是会以单线程的方式执行这些命令操作。

阶段四:IO线程回写Socket和主线程清空全局队列

        当主线程执行完请求操作后,会把需要返回的结果写入到缓冲区,然后,主线程会阻塞等待IO线程,把这些结果写回到Socket中,并返回给客户端。和IO线程读取和解析请求一样,IO线程回写Socket时,也是有多个线程在并发执行,所以回写Socket的速度也很快。等到IO线程回写Socket完毕,主线程会清空全局队列,等待客户端的后续请求。

3.Unix网络中五种IO模型

  • Blocking IO - 阻塞IO
  • NoneBlocking IO - 非阻塞IO
  • IO multplexing - IO多路复用
  • signal driven IO - 信号驱动IO
  • asynchronous IO - 异步IO

IO multplexing - IO 多路复用

Linux世界中一切皆文件:文件描述符:简称FD,句柄

  File descriptor:文件描述符(File descriptor)是计算机科学中的一个术语,是一个用于表述指向文件的引入的抽象化概念。文件描述符在形式上是一个非负整数。实际上,它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。在程序设计中,文件描述符这一概念往往只适用于UNIXLinux这样的操作系统。

4.IO多路复用

        一种同步的IO模型,实现一个线程监视多个文件句柄一旦某个文件句柄就绪就能够通知到对应应用程序进行响应的读写操作,没有文件句柄就绪时会阻塞应用程序,从而释放CPU资源。

        总而言之就是:I/O多路复用是一种高效的I/O操作机制,用于同时监视多个I/O流的可读性、可写性或异常情况,而无需为每个I/O流分配一个单独的线程或进程。这种技术允许一个单独的线程或进程来管理多个I/O操作,从而减少了系统资源的消耗并提高了系统的响应性和吞吐量。

4.1 概念

  • I/O:网络I/O,尤其在操作系统层面指数据在内核态和用户态之间的读写操作。
  • 多路多个客户端连接(连接就是套接字描述符,即socket 或者 channel)
  • 复用:复用一个或几个线程。
  • IO多路复用:也就是说一个或一组线程处理多个TCP连接,使用单进程就能够实现同时处理多个客户端的连接,无需创建或者维护过多的进程/线程。

一个服务端进程可以同时处理多个套接字描述符,实现IO多路复用的模型有三种:可以分select -> poll ->epoll三个阶段来描述。

select():select() 是一种比较早的I/O多路复用机制,它允许程序员监视一组文件描述符,并在其中任何一个文件描述符就绪时进行通知。但是select()有一些限制,比如它通常有最大文件描述符数量的限制,并且在大量文件描述符的情况下性能较差。

poll():poll() 是对select()的改进,它也可以同时监视多个文件描述符,但没有select()的一些限制,poll()使用一个pollfd结构数组来传递需要监视的文件描述符和所关注的事件。

epoll:epoll()是Linux特有的I/O多路复用机制,它提供了更高效的时间通知机制。相比较select()poll()epoll()在处理大量文件描述符时具有更好的性能。epoll()使用一个epoll实例来管理需要监视的文件描述符,并通过epoll_ctl()来添加、删除或者修改监视的文件描述符。然后通过epoll_wait()来等待就绪时间的发送。

I/O多路复用的优势在于,它避免了创建多个线程或者进程来处理I/O操作,从而减少了上下文切换的开销,提高了系统的性能和资源利用率,这种技术常用于网络编程中,用于处理大量并发连接的情况,例如WEB服务器、聊天服务器等。

4.2 IO多路复用模型

场景分析

        模拟一个tcp服务器处理30个客户端socket ,假设你是一个监考老师,让30个学生解答一道竞赛考题,然后负责验收学生答卷,你有下面几个选择:

        第一种选择(轮询):按照顺序逐个验收,先验收A、然后是B,之后是C、D。。。这中间如果有一个学生卡主了,全班都会被耽误,你用循环挨个处理socket,根本不具有并发能力。

        第二种选择(来一个new一个,一对一服务):你创建30个分身线程,每个分身线程检查一个学生的答案是否正确。这种类似于为每个用户创建一个进程或者线程处理连接。

        第三种选择(响应式处理,1对多服务):你站在讲台上等,谁解答完谁举手。这时C、D举手,表示他们解答问题完毕,你下去依次检查C、D的答案,然后继续回到讲台上等。此时E、A又举手,然后去处理E和A。。。这种就是IO复用模型Linux下的selectpollepoll就是干这个的。

IO多路复用模型

        将用户socket对应的文件描述符(FileDescriptor)注册进epoll,然后epoll帮你监听哪些socket上有消息到达,这样就避免了大量的无用操作。此时的socket应该采用非阻塞模式。这样,整个过程只在调用selectpollepoll这些调用的时候才会堵塞,收发客户消息是不会阻塞的,整个进程或线程就被充分利用起来,这就是事件驱动,所谓的reactor反应模式。

        在单个线程通过记录跟踪每一个Socket(I/O流)的状态俩同时管理多个I/O流,一个服务端进程可以同时处理多个套接字描述符。目的是尽量的提高服务器的吞吐能力。

        大家都用过nignxnginx使用epoll接收请求,nginx会有很多请求过来,epoll会把他们都监视起来,然后像拨开关一样,谁有数据就拨向谁,然后调用相应的代码处理。redis类似同理,这就是I/O多路复用原理,有请求就响应,没有请求不打扰。

4.3 Reactor模型

        Redis服务采用Reactor的方式来实现文件事件处理器(每一个网络连接其实都对应一个文件描述符)

        Redis基于Reactor模式开发了网络事件处理器,这个处理器被称为文件事件处理器。它的组成结构为4部分:

        多个套接字、IO多路复用程序、文件时间分配器、事件处理器,因为文件事件分派器队列的消费是单线程的,所以Redis才叫单线程模型。

        基于I/O复用模型:多个连接共用一个阻塞对象,应用程序只需要在一个阻塞对象上等待,无需阻塞等待所有连接。当某条连接有新的数据可以处理时,操作系统通知应用程序,线程从线程阻塞状态返回,开始进行业务处理。

        Reactor模式,是指通过一个或者多个输入同时传递给服务器的服务请求的事件驱动处理模式。服务端程序处理传入多路请求,并将它们同步分派给请求对应处理线程,Reactor模式也叫Dispatcher模式,即I/O多路复用统一监听事件,收到事件后分发(Dispatch 给某进程),是编写高性能网络服务器的必备技术。

Reactor模式中有2个关键组成:

  1. Reactor:Reactor在独立的线程中运行,负责监听和分发事件,分发给适当的处理程序对IO事件做出反应。它就像公司的电话接线员,它接听来自客户的电话并将线程转移到适当的联系人;
  2. Handlers:处理程序执行I/O事件要完成的实际事件,类似于客户想要与之交谈的公司的实际办理人。Reactor通过调度适当的处理程序来响应I/O事件,处理程序执行非阻塞操作。

三、结论

  Redis工作线程是单线程的,但是对于整个Redis来说是多线程的。

  I/O的读和写本身就是堵塞的,比如当scoket中有数据时,Redis会通过调用先将数据从内核空间拷贝到用户态空间,再交给Redis调用,而这个拷贝的过程就是阻塞的,当数量越大时拷贝所需要的时间就越多,这些操作都是基于单线程完成的。

        从Redis6开始,就新增了多线程的功能来提高I/O的读写性能,他的主要实现思路是将主线程的IO读写任务拆分给一组独立的线程去执行,这样就可以使多个socket的读写可以并行化了,采用多路I/O复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络IO的时间消耗),将最耗时的Scoket的读取、请求解析、写入单独外包出去,剩下的命令执行让然由主线程串行执行并和内存的数据交互。

结合上图可知,网络IO操作就变成多线程化了,其他核心部分仍然是线程安全的,是一个不错的折中办法。

  Redis6 --> 7将网络数据读写、请求协议解析通过多个IO线程来处理,对于真正的命令执行来说,仍然使用主线程操作,一举两得。

22

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/799173.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目架构MVC,DDD学习

写在前面 本文一起看下项目架构DDD,MVC相关的内容。 1:MVC 不管我们做什么项目,自己想想其实只是做了三件事,如下: 其实,这三件事完全在一个类中做完也可以可以正常把项目完成的,就像下面这…

【MacBook系统homebrew镜像记录】

安装 使用Homebrew 国内源安装脚本,贼方便: /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"切换至清华大学镜像源: 命令合并: 分别切换了 brew.git、 homebrew-core.git、 homebrew-…

逆向案例十二——看准网企业信息json格式的信息

网址:【全国公司排行|排名榜单|哪家好】-看准网 打开开发者工具——刷新——网络——XHR——下滑页面加载新的页面——找到数据包 发现参数加密,返回的数据也进行了加密 按关键字在下方搜索 kiv进入第一个js文件 ctrlf打开文件里面的搜索框继续搜kiv找到…

Java 面试宝典:Redis 的线程模型是怎么样的?

大家好,我是大明哥,一个专注「死磕 Java」系列创作的硬核程序员。 本文已收录到我的技术网站:https://www.skjava.com。有全网最优质的系列文章、Java 全栈技术文档以及大厂完整面经 Redis 的线程模型其实是分两块的: Redis 6.0 …

前端开发语言有那些?

前端开发语言有那些? 1、html 超文本标记语言:构建前端网页的基本结构,就象人的骨架一样。 2、css 层叠样式表:控制网页的样式和布局,就象人需要穿各种服式展现不同风采。 3、javascript 简称 JS 动态脚本语言&#x…

阿里面试总结

ThreadLocal 线程变量存放在当前线程变量中,线程上下文中,set将变量添加到threadLocals变量中 Thread类中定义了两个ThreadLocalMap类型变量threadLocals、inheritableThreadLocals用来存储当前操作的ThreadLocal的引用及变量对象,把当前线程…

括号串(Deque)

题目 import java.util.Deque; import java.util.LinkedList; import java.util.Scanner; public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();sc.nextLine();char[] c sc.nextLine().toCharArray();Deque…

Flutter第六弹 基础列表ListView

目标: 1)Flutter有哪些常用的列表组建 2)怎么定制列表项Item? 一、ListView简介 使用标准的 ListView 构造方法非常适合只有少量数据的列表。我们还将使用内置的 ListTile widget 来给我们的条目提供可视化结构。ListView支持…

性能分析--内存知识

内存相关知识 计算机中与CPU进行数据交换的桥梁。内存的速度,比CPU的速度要慢很多。比磁盘速度要快很多。内存中存放数据,一旦断电就会消失。linux系统的 /proc路径下的文件,都是内存文件。内存大小,一般 是GB为单位。 现在都操作…

WebKit是什么?

WebKit是一个开源的浏览器引擎,它用于呈现网页内容在许多现代浏览器中,包括Safari浏览器、iOS内置浏览器、以及一些其他浏览器如Google Chrome的早期版本。以下是一些关于WebKit的重要信息: 起源和发展:WebKit最初是由苹果公司为其…

K8s学习四(资源调度_1)

资源调度 发现对Pod操作不方便,不能直接操作,而且不能直接编辑,需要对原来的配置文件进行操作,而且需要删除之后再创建Pod,不方便,更多是通过控制器来操作。 Label和Selector 通过设置标签和选择器来确定…

Python爬虫:为什么你爬取不到网页数据

目录 前言 一、网络请求被拒绝 二、数据是通过JavaScript加载的 三、需要进行登录 四、网站反爬虫策略 五、网站结构变更 总结 前言 作为一名开发者,使用Python编写爬虫程序是一项常见的任务。爬虫程序的目的是收集互联网上的数据,并将其保存或使…

解决IDEA 控制台中文乱码

运行某个项目时IntelliJ IDEA 控制台中文乱码,但其他的项目是正常的。接口文档也显示乱码: 一、修改 IntelliJ IDEA 全局编码、项目编码、属性文件编码 上方导航栏“File→Settings…”进入配置页面,在“Editor”中下滑找到“File Encodings…

LeetCode 面试题 02.07.链表相交(判断两个结点是否相同)

给你两个单链表的头节点 headA 和 headB ,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。 图示两个链表在节点 c1 开始相交: 题目数据 保证 整个链式结构中不存在环。 注意,函数返回结果后&#x…

内外网数据交换发展进程:安全与便捷并行

随着信息化的不断推进,医院、党政以及企业的内外网数据交换正成为日益关注的焦点。在保障数据安全的前提下,需要寻求一种既安全可靠又操作便捷的数据传输方式。本文将探讨内外网数据交换发展进程,分析各种传输方式的优缺点,以及它…

麒麟系统ARM安装rabbitmq

简单记录下,信创服务器:麒麟系统,安装rabbitmq的踩坑记录。 本文章参考了很多大佬文章,我整理后提供。 一、安装基础依赖 yum -y install make gcc gcc-c kernel-devel m4 ncurses-devel openssl-devel unixODBC-devel 二、下载…

k8s资源监控_bitnami metrics-server v0(1),2024一位Linux运维中级程序员的跳槽面经

错误3 也有可能会遇到以下错误,按照下面提示解决 Error from server (ServiceUnavailable): the server is currently unable to handle the request (get nodes.metrics.k8s.io) 如果metrics-server正常启动,没有错误,应该就是网络问题。修改…

花一分钟简单认识 CSS 中的规则 —— 级联层 @layer

layer 简介: 声明级联层时,越靠后优先级越高。不属于任何级联层的样式,将自成一层匿名级联层,并置于所有层之后 —— 级别最高。 用法一:在同一文件中 layer base, special; layer special {/* 优先 */li { color: …

Python学习笔记——heapq

堆排序 思路 堆排序思路是: 将数组以二叉树的形式分析,令根节点索引值为0,索引值为index的节点,子节点索引值分别为index*21、index*22;对二叉树进行维护,使得每个非叶子节点的值,都大于或者…

2024-4-7 QT day1作业

myWidget.cpp #include "mywidget.h"MyWidget::MyWidget(QWidget *parent): QWidget(parent) {//设置窗口标题this->setWindowTitle("QQ");//设置窗口图标this->setWindowIcon(QIcon("C:\\Users\\张谦\\Desktop\\pictrue\\qq.png"));//设…