范文健康探索娱乐情感热点
投稿投诉
热点动态
科技财经
情感日志
励志美文
娱乐时尚
游戏搞笑
探索旅游
历史星座
健康养生
美丽育儿
范文作文
教案论文
国学影视

Redis属于单线程还是多线程?不同版本之间有什么区别?

  Redis 是单线程还是多线程
  Redis 应该是使用频率最高的组件之一了,不仅在工作中会大量使用,面试的时候也经常会作为考点出现,下面就来深入地了解一下 Redis。
  先来探讨一个问题,Redis 使用的到底是多线程还是单线程?
  不同版本的 Redis 是不同的,在 4.0 之前 Redis 是单线程运行的,但是单线程并不代表效率低。像 Nginx、Nodejs 也是单线程程序,但它们的效率并不低,因为底层采用了基于 epoll 的 IO 多路复用(后面说)。
  此外 Redis 是基于内存操作的,它的瓶颈在于机器的内存、网络带宽,而不是 CPU,因为在你 CPU 还没达到瓶颈时你的内存可能就先满了、或者带宽达到瓶颈了。因此 CPU 不是主要原因,那么自然就采用单线程了。更何况使用多线程还会面临一些额外的问题,比如共享资源的保护等等,对于一个 CPU 不是主要瓶颈的键值对数据库而言,采用单线程是非常合适的。
  简单来说,Redis 在 4.0 之前使用单线程的模式是因为以下三个原因: 使用单线程模式的 Redis,其开发和维护会更简单,因为单线程模型方便开发和调试; 即使使用单线程模型也能够并发地处理多客户端请求,因为 Redis 内部使用了基于 epoll 的多路复用; 对于 Redis 而言,主要的性能瓶颈是内存或者网络带宽,而并非 CPU;
  但 Redis 在 4.0 以及之后的版本中引入了惰性删除(也叫异步删除),这是由额外的线程执行的。意思就是我们可以使用异步的方式对 Redis 中的数据执行删除操作了,例如:unlink key, flushdb async, flushall async,举个例子: 127.0.0.1:6379> set name satori OK 127.0.0.1:6379> get name "satori"  # 这里是异步删除一个 key # 同步的话则是 del name 127.0.0.1:6379> unlink name   (integer) 1 127.0.0.1:6379> flushdb async OK 127.0.0.1:6379> flushall async OK
  这样处理的好处就是不会使 Redis 的主线程卡顿,会把这些删除操作交给后台线程来执行。
  通常情况下使用 del 指令可以很快地删除数据,但是当被删除的 key 是一个非常大的对象时,例如:删除的是包含了成千上万个元素的 hash 集合,那么 del 指令就会造成 Redis 主线程卡顿。而使用惰性删除,可以有效地避免 Redis 卡顿的问题。
  除了惰性删除,像持久化、集群数据同步等等,都是由额外的子线程执行的,而 Redis 主线程则专注于网络 IO 和键值对读写。 单线程的 Redis 为什么这么快
  正如上面所说,Redis4.0 之前是单线程的,那既然是单线程为什么速度还能那么快?吞吐量还能那么高?
  原因有以下几点:
  1)基于内存操作:Redis 的所有数据都在内存中,因此所有的运算都是内存级别的,所以它的性能比较高;
  2)数据结构简单:Redis 的数据结构是为自身专门量身打造的,而操作这些数据结构的时间复杂度是 O(1);
  3)多路复用和非阻塞 I/O:Redis 使用 I/O 多路复用功能来监听多个 socket 连接客户端,这样就可以使用一个线程来处理多个连接,从而减少线程切换带来的开销,同时也避免了 I/O 阻塞操作,从而大大地提高了 Redis 的性能;
  4)避免上下文切换:因为是单线程模型,因此就避免了不必要的上下文切换和多线程竞争,这就省去了多线程切换带来的时间和性能上的开销,而且单线程不会导致死锁的问题发生; 非阻塞 I/O 和 I/O 多路复用是什么
  首先我们可以使用 get 命令,获取一个 key 对应的 value,比如: 127.0.0.1:6379> get name "satori"
  那么问题来了,以上对于 Redis 服务端而言,都发生了哪些事情呢?
  服务端必须要先监听客户端连接(bind/listen),然后当客户端到来时与其建立连接(accept),从 socket 中读取客户端的请求(recv),对请求进行解析(parse)。这里解析出的请求类型是 get、key 是 "name",再根据 key 获取对应 value,最后返回给客户端,也就是向 socket 写入数据(send)。
  以上所有操作都是由 Redis 主线程依次执行的,但里面会有潜在的阻塞点,分别是 accept 和 recv。
  当 Redis 监听到一个客户端有连接请求、但却一直未能成功建立连接,那么主线程会一直阻塞在 accept 函数这里,导致其它客户端无法和 Redis 建立连接。
  类似的,当 Redis 通过 recv 从客户端读取数据时,如果数据一直没有到达,那么 Redis 主线程也会一直阻塞在 recv 这一步,因此这就导致了 Redis 的效率会变得低下。
  非阻塞 I/O
  但很明显,Redis 不会允许这种情况发生,因为以上都是阻塞 I/O 会面临的情况,而 Redis 采用的是非阻塞 I/O,也就是将 socket 设置成了非阻塞模式。
  首先在 socket 模型中,调用 socket() 方法会返回主动套接字,调用 bind() 方法绑定 IP 和 端口,再调用 listen() 方法将主动套接字转化为监听套接字,最后监听套接字调用 accept() 方法等待客户端连接的到来,当和客户端建立连接时再返回已连接套接字,而后续就通过已连接套接字来和客户端进行数据的接收与发送。
  但是注意:我们说在 listen() 这一步,会将主动套接字转化为监听套接字,而此时的监听套接字的类型是阻塞的,阻塞类型的监听套接字在调用 accept() 方法时,如果没有客户端来连接的话,就会一直处于阻塞状态,那么此时主线程就没法干其它事情了。
  所以在 listen() 的时候可以将其设置为非阻塞,而非阻塞的监听套接字在调用 accept() 时,如果没有客户端连接到达时,那么主线程就不会傻傻地等待了,而是会直接返回,然后去做其它的事情。
  类似的,我们在创建已连接套接字的时候也可以将其类型设置为非阻塞,因为阻塞类型的已连接套接字在调用 send() / recv() 的时候也会处于阻塞状态。比如当客户端一直不发数据的时候,已连接套接字就会一直阻塞在 recv() 这一步。如果是非阻塞类型的已连接套接字,那么当调用 recv() 但却收不到数据时,也不用处于阻塞状态,同样可以直接返回去做其它事情。
  但是有两点需要注意:
  1)虽然 accept() 不阻塞了,在没有客户端连接时 Redis 主线程可以去做其它事情,但如果后续有客户端连接,Redis 要如何得知呢?因此必须要有一种机制,能够继续在监听套接字上等待后续连接请求,并在请求到来时通知 Redis。
  2)send() / recv() 不阻塞了,相当于 I/O 读写流程不再是阻塞的,读写方法都会瞬间完成并返回,也就是说它会采用能读多少就读多少、能写多少就写多少的策略来执行 I/O 操作,这显然更符合我们对性能的追求。
  但这样会面临一个问题,那就是当我们执行读取操作时,有可能只读取了一部分数据,剩余的数据客户端还没发过来,那么这些数据何时可读呢?同理写数据也是这种情况,当缓冲区满了,而我们的数据还没有写完,那么剩下的数据又何时可写呢?因此同样要有一种机制,能够在 Redis 主线程做别的事情的时候继续监听已连接套接字,并且在有数据可读写的时候通知 Redis。
  这样才能保证 Redis 线程既不会像基本 IO 模型一样,一直在阻塞点等待,也不会无法处理实际到达的客户端连接请求和可读写的数据,而上面所提到的机制便是 I/O 多路复用。
  I/O 多路复用
  I/O 多路复用机制是指一个线程处理多个 IO 流,也就是我们经常听到的 select/poll/epoll,而 Linux 默认采用的是 epoll。
  简单来说,在 Redis 只运行单线程的情况下,该机制允许内核中同时存在多个监听套接字和已连接套接字。内核会一直监听这些套接字上的连接请求或数据请求,一旦有请求到达就会交给 Redis 线程处理,这样就实现了一个 Redis 线程处理多个 IO 流的效果。
  上图就是基于多路复用的 Redis IO 模型,图中的 FD 就是套接字,可以是监听套接字、也可以是已连接套接字,Redis 会通过 epoll 机制来让内核帮忙监听这些套接字。而此时 Redis 线程或者说主线程,不会阻塞在某一个特定的套接字上,也就是说不会阻塞在某一个特定的客户端请求处理上。因此 Redis 可以同时和多个客户端连接并处理请求,从而提升并发性。
  但为了在请求到达时能够通知 Redis 线程,epoll 提供了基于事件的回调机制,即针对不同事件的发生,调用相应的处理函数。
  那么回调机制是怎么工作的呢?以上图为例,首先 epoll 一旦监测到 FD 上有请求到达,就会触发相应的事件。这些事件会被放进一个队列中,Redis 主线程会对该事件队列不断进行处理,这样一来 Redis 就无需一直轮询是否有请求发生,从而避免资源的浪费。
  同时,Redis 在对事件队列中的事件进行处理时,会调用相应的处理函数,这就实现了基于事件的回调。因为 Redis 一直在对事件队列进行处理,所以能及时响应客户端请求,提升 Redis 的响应性能。
  比如连接请求和数据读取请求分别对应 Accept 事件和 Read 事件,Redis 分别对这两个事件注册 accept 和 get 回调函数。当 Linux 内核监听到有连接请求或数据读取请求时,就会触发 Accept 事件或 Read 事件,然后内核就会回调 Redis 注册的 accept 函数或 get 函数。
  就像病人去医院看病,在医生实际诊断之前每个病人(类似于请求)都需要先分诊、测体温、登记等等。如果这些工作都由医生完成,那么医生的工作效率就会很低。所以医院设置了分诊台,分诊台会一直处理这些诊断前的工作(类似于 Linux 内核监听请求),然后再转交给医生做实际诊断,这样即使一个医生(相当于 Redis 的主线程)也能有很高的效率。
  需要注意的是,不同的操作系统有着不同的多路复用实现,除了 Linux 的 epoll,还有 FreeBSD 的 kqueue、以及 Solaris 的 evport。 相关视频推荐
  源码调试:redis io多线程
  redis 为什么是单线程?这里单线程指什么?redis单线程为什么这么快?
  需要C/C++ Linux服务器架构师学习资料加qun 812855908 获取(资料包括 C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg 等),免费分享
  嗦一下 Redis 6.0 的多线程
  Redis 6.0 引入了一些新特性,其中非常受关注的一个特性就是多线程。在 4.0 之前 Redis 是单线程的,因为单线程的优点很明显,不但降低了 Redis 内部实现的复杂性,也让所有操作都可以在无锁的情况下进行,并且不存在死锁和线程切换带来的性能以及时间上的消耗。
  但是其缺点也很明显,单线程机制导致 Redis 的 QPS(Query Per Second,每秒查询数)很难得到有效的提高(虽然已经够快了,但人毕竟还是要有更高的追求的)。
  然后 Redis 在 4.0 版本引入了多线程,但是此版本的多线程主要用于大数据量的异步删除,对于非删除操作的意义并不是很大。
  而 Redis 6.0 中的多线程则是真正为了提高 I/O 的读写性能而引入的,它的主要实现思路是将主线程的 I/O 读写任务拆分给一组独立的子线程去执行。也就是说,从 socket 读数据和写数据不再由主线程负责,而是交给了多个子线程,这样就可以使多个 socket 的读写并行化了。这么做的原因就在于,虽然在 Redis 中使用了 I/O 多路复用和非阻塞 I/O,但数据在内核态空间和用户态空间之间的拷贝是无法避免的,而数据的拷贝这一步是阻塞的,并且当数据量越大时拷贝所需要的时间就越多。
  所以 Redis 在 6.0 引入了多线程,用于分摊同步读写 I/O 压力,从而提升 Redis 的 QPS。
  但是注意:Redis 的命令本身依旧是由 Redis 主线程串行执行的,只不过具体的读写操作交给独立的子线程去执行了(一会儿详细说明 Redis 的主线程和子线程之间是如何协同的)。而这么做的好处就是不需要为 Lua 脚本、事务的原子性而额外开发多线程互斥机制,这样一来 Redis 的线程模型实现起来就简单多了。因为和之前一样,所有的命令依旧是由主线程串行执行,只不过具体的读写任务交给了子线程。
  除了引入多线程,还可以将内核网络协议栈换成用户态网络协议栈(DPDK),让网络请求不在内核里进行,直接在用户态完成。但 Redis 并没有采用这种做法,虽然替换协议栈可以避免频繁地让内核参与网络请求处理,提升请求处理效率。原因是该做法要求 Redis 添加对用户态网络协议栈的支持,需要修改 Redis 源码中和网络相关的部分,这会带来很多额外的开发工作量;而且新增代码还可能引入 bug,导致 Redis 程序不稳定,因此 Redis 6.0 没有采用这种做法。 请再具体嗦一下 Redis 6.0 的主线程和子线程之间是如何协同的?
  整体可以分为四个阶段。
  阶段一:服务端和客户端建立 socket 连接,并分配子线程(处理线程)
  首先,主线程负责建立连接请求,当有客户端请求到达时,主线程会创建和客户端的 scoket 连接,该 socket 连接就是用来和客户端进行数据传输的。只不过这一步不由主线程来做,主线程要做的事情是将该 socket 放入到全局等待队列中,然后通过轮询的方式选择子线程,并将队列中的 socket 连接分配给它。
  所以无论是从客户端读数据还是向客户端写数据,都由子线程来做。因为我们说 Redis 6.0 引入的多线程就是为了缓解主线程的 I/O 读写压力,而 I/O 读写这一步是阻塞的,所以应该交给子线程并行操作。
  阶段二:子线程读取并解析请求
  主线程一旦把 socket 连接分配给子线程,那么会进入阻塞状态,等待子线程完成客户端请求的读取和解析,得到具体的命令操作。由于可以有多个子线程,所以这个操作很快就能完成。
  阶段三:主线程执行命令操作
  等到子线程读取到客户端请求并解析完毕之后,再由主线程以单线程的方式执行命令操作,所以 I/O 读写虽然交给了子线程,但命令本身还是由 Redis 主线程执行的。
  阶段四:子线程回写 socket、主线程清空全局队列
  当主线程执行完命令操作时,还需要将结果写入缓冲区,而这一步显然要由子线程来做,因为是 I/O 读写。此时主线程会再次陷入阻塞,直到子线程将这些结果写回 socket 并返回给客户端。
  和读取一样,子线程将数据写回 socket 时,也是多个线程在并行执行,所以写回 socket 的速度也很快。之后主线程会清空全局队列,等待客户端的后续请求。
  在 Redis 6.0 中如何开启多线程?
  在了解了 Redis 6.0 的多线程机制之后,我们要如何开启多线程呢?在 Redis 6.0 中,多线程机制默认是关闭的,如果想启动的话,需要修改 redis.conf 里的两个配置。 # 设置 io-thread-do-reads 配置项为 yes # 表示启用多线程 io-thread-do-reads yes # 通过 io-threads 设置子线程的数量 io-threads 3
  上述配置表示开启 3 个子线程,需要注意的是,线程数并不是越大越好,应该小于机器的 CPU 核数。关于线程数的设置,官方的建议是:如果 4 核的 CPU,那么设置子线程数为 2 或 3;如果 8 核的 CPU,那么设置子线程数为 6。
  如果你在实际应用中,发现 Redis 实例的 CPU 开销不大,吞吐量却没有提升。那么可以考虑使用 Redis 6.0 的多线程机制,加速 IO 读写处理,进而提升实例的吞吐量。
  最后关于 Redis 的性能,Redis 的作者在 2019 的 RedisConf 大会上提到,Redis6.0 引入的多线程 I/O 特性对性能的提升至少是一倍以上。国内也有人在阿里云使用 4 个线程的 Redis 版本和单线程的 Redis 版本进行比较测试,发现测试的结果和 Redis 作者说的一致,性能基本可以提高一倍。 小结
  以上我们就介绍了 Redis 在 4.0 之前明明采用单线程但却依然快的原因:基于内存操作、量身打造的数据结构、I/O 多路复用和非阻塞 I/O、避免了不必要的线程上下文切换。
  并且在 Redis4.0 开始支持多线程,主要体现在大数据的异步删除上面,例如:unlink key、flushdb async、flushall async 等。
  而 Redis6.0 的多线程则增加了对 I/O 读写的并发能力,因为数据在用户态和内核态之间穿梭是需要进行拷贝的,而这一步会阻塞,所以通过多个线程并行操作能更好地提升 Redis 的性能。

秋天的经典搭配,紧身牛仔裤搭配长靴,保暖又很时尚秋天好看又精致的搭配,可以尝试一下紧身牛仔裤搭配长靴,会有不一样的效果,如果你可以轻松驾驭高跟鞋,可以选择高跟长靴,穿搭也要根据自己的实际情况来搭配,这位美女选择一双黑色长靴,平底Gucci品牌代言人肖战,时尚就是自信GUCCI微博账号在9月23号晚上10点发的动态(见下图),视频里面的肖战真的状态超好气质很高级。今年是他与古驰携手合作的第二年,古驰作为高奢品牌,于2021年10月22日官宣肖战东莞炒粉姐走红,穿着时尚令人侧目,网友她还有自己的事业秋日生活打卡季如今这个社会,但凡有人穿着精致点做事,就有可能会引人非议东莞大朗水口市场的炒粉姐,每天穿着时尚出摊,意外走红网络后却引来他人非议,质疑她是在摆拍,炒粉纯粹就是在作秀!包萝卜饺子,萝卜别再焯水了,教你一招,萝卜又香又脆,鲜美多汁包萝卜饺子,萝卜别再焯水了,教你一招,萝卜又香又脆,鲜美多汁进入秋冬季后,饺子是比较受欢迎的主食,一碗饺子有饭有菜还有汤,吃得很舒服,吃得全身热乎乎的的。饺子营养丰富,含有大量蛋白十一出行,这5款解馋零食记得带,旅途堵车不堵心,比泡面靠谱马上就要到十一小长假,想必大家这会已经开始安排自己的假期行程。很多朋友为方便都会选择自驾出游,不过堵车这个问题就很让人头疼,这个时候各种干粮更要准备妥当!大多数人出远门的标配就是泡今晨,天安门广场祝福祖国巨型花果篮亮相经过深夜吊装,天安门广场上的祝福祖国巨型花果篮今晨与市民游客见面了。整个国庆摆花工作将在9月25日全部完成。正值周末,不少市民赶早儿来到天安门广场,只为一睹花果篮的芳容。这座巨型花要论城市里好吃又好逛的地儿,还得是这儿朋友们,作为一个杭州人,每当被问杭州哪里适合逛街啊?我都会一时语塞,然后给出嘉里中心湖滨in77这样毫无新意的答案。别误会,西湖边的商圈当然是好逛的。只是全国商圈都开始同质化了,入广州出发贺州2天游,走进黄姚古镇,畅游紫云洞东潭岭玉泉洞天今天给大家介绍一条广西贺州高铁2天游行程,黄姚古镇静享慢时光,潭岭俯瞰全景黄姚,行摄紫云仙境。我们的目的地黄姚古镇曾经有人这样形容过黄姚黄姚古镇如同一本千年的诗集,被人遗忘在图书馆喜迎二十大忠诚保平安西安旅游警察为游客当好平安旅程护航员来源西安公安维护市场秩序守护游客平安严打涉旅违法犯罪。近年来,西安市公安局建设旅游警察队伍,助力西安擦亮千年古都常来长安旅游金招牌。在党的二十大即将召开之际,西安市公安局总结工作亮最新消息,长沙南部片区包爷庙的来历诗人朱文怡明朝永乐三年(1405年)也就是郑和第一次下西洋的这一年,在中国大地上继洪武年间的人口大迁移后又再次掀起了人口迁移政策。自永乐初年起,湖南等地也陆陆续续迎来一些北方人氏前云蒙山地质公园作者王海先云蒙山,被称为小黄山,在密云和怀柔交界处,古称云梦山,是京郊著名的风景名胜区,也是北京市著名的国家级森林公园。境内山势耸拔,沟谷切割幽深,奇峰异石多姿,林木花草馥郁,自然
联手华为!江苏省公布重磅名单,东南大学硬核项目入围,系高校首个!近日,江苏省工业和信息化厅公布了2022年智慧江苏重点工程和标志性工程项目名单,东南大学数智东南智慧校园建设与应用项目成功入围智慧江苏10项标志性工程之一,是自智慧江苏标志性工程评英格兰将在10日公布世界杯名单菲利普斯和沃克待定北京时间昨晚11点,曼城在主场迎战富勒姆。凭借哈兰德最后时刻点射破门,最终10人曼城21击败富勒姆。赛后曼城主帅瓜迪奥拉在接受采访时谈到了队内中场菲利普斯和后卫凯尔沃克的伤势恢复情外媒走读肇庆澳门日报聆听红色故事,感受肇庆乡村振兴之路澳门日报消息由粤港澳大湾区十家主流媒体代表组成的2022外媒走读肇庆采访团,11月3日顺利完成在肇庆为期四天的参观采访行程,聆听当地红色故事,感受实践乡村振兴之路。岗坪镇立足粤桂两新华全媒丨吉林莫莫格迎来候鸟南迁高峰11月4日,候鸟从吉林省白城市镇赉县莫莫格国家级自然保护区上空飞过。近日,栖息于吉林省白城市镇赉县莫莫格国家级自然保护区的候鸟进入南迁高峰期。莫莫格湿地是我国候鸟迁徙路上的重要停歇平邑县历史上第一位女县委书记,李玉妹,你还记得吗1991年3月,时任临沂地委委员共青团临沂地委书记的李玉妹履新平邑,担任临沂地委委员兼平邑县委书记,成为平邑县历史上第一位女县委书记。上任后不久,以李玉妹为班长的县委,做出了建设莲1973年,山西一村民借着酒劲钻进了百墓坡,被吓得连滚带爬了出来1973年,山西运城,一村民借着酒劲钻进了百墓坡,正当他绷紧神经在寻找金银宝贝的时候,突然发现在一扇朱漆大门的后面,有一个妇人在向他招手微笑,仿佛在说来呀,快过来!村民吓得瘫坐在地如何评价民国各个军阀头子?马继援(19212012),甘肃河州人,回族,西北王马步芳长子。国共内战,蒋介石为了西北王马步芳,一日,蒋介石带儿子蒋经国在总统府会接见了马步芳和他的儿子马继援。见面时,蒋介石介绍历史上第一位男皇后究竟有多美各位客官的点赞和关注,是高高写文章的动力。容貌艳丽纤妍洁白螓首膏发自然蛾眉,见者靡不啧啧。这是三言作者冯梦龙对这位男皇后韩子高的赞美。l韩子高到底美到何种程度呢,据说侯景之乱时,年沉鱼落雁闭月羞花三国时汉献帝的大臣司徒王允的歌妓貂婵是闭月的代称。她能歌善舞,很受王允的宠爱。当时,董卓专权,挟天子以令诸侯,大臣们敢怒而敢言。王允每天闷闷不乐,茶不饮,饭不进。貂婵很为主人忧愁。千古第一毒后吕雉,背后的秘密千古第一毒后吕雉,背后的秘密她因为父亲的一面之好,不顾母亲反对把年仅17岁的她嫁给了三十多岁还在骗吃骗喝的沛县末吏刘邦由此拉开了苦难一生的序幕。婚后因为醉酒致使罪犯逃脱,刘邦躲至芒嘉靖为什么20多年不上朝?大明却依然屹立不倒嘉靖帝是一个比较奇葩的皇帝了!他有二十多年没上朝!可是后世对他的评价却并不差!严格意义上来说,在明朝能真正把权力牢牢掌控在手上的皇帝只有三个!明太祖和明成祖自不用说,还有一个就是我