怎么解析Redis6中的单线程和多线程模型

这篇文章的内容主要围绕怎么解析Redis6中的单线程和多线程模型进行讲述,文章内容清晰易懂,条理清晰,非常适合新手学习,值得大家去阅读。感兴趣的朋友可以跟随小编一起阅读吧。希望大家通过这篇文章有所收获!

1. Redis演进史

如果单纯的说redis是单线程或多线程,这个回答肯定不严谨,不同版本使用的线程模型是不一样的。【相关推荐:Redis视频教程】

怎么解析Redis6中的单线程和多线程模型  redis6 第1张

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

  • 版本4.x,严格意义来说也不是单线程,而是负责处理客户端请求的线程是单线程,但是 开始加了点多线程的东西(异步删除)

  • 最新版本的6.0.x后, 告别了大家印象中的单线程,用一种全新的多线程来解决问题。

2. Redis单线程模型

2.1 单线程真实含义

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

但Redis的其他功能, 比如持久化、异步删除、集群数据同步等等,其实是由额外的线程执行的。 可以这么说,Redis工作线程是单线程的。但是,整个Redis来说,是多线程的

2.2 单线程性能快原因

Redis 3.x 单线程时代但是性能很快的主要原因

  • 基于内存操作:所有数据都存在内存中,因此所有的运算都是内存级别的

  • 数据结构简单:Redis的数据结构是专门设计的,而这些简单的数据结构的查找和操作的时间大部分复杂度都是o(1)

  • 多路复用和费阻塞IO:使用IO多路复用功能监听多个socket连接客户端,这样就可以使用一个线程连接处理多个请求,减少线程切换带来的开销,同时避免IO阻塞操作

  • 避免上下文切换:因为是单线程模型,就可以避免不必要的上先文切换和多线程竞争,这样可以省去多线程切换带来的时间和性能上的消耗,而且单线程不会导致死锁问题的发生

2.3 采用单线程原因

Redis 是基于内存操作的, 因此他的瓶颈可能是机器的内存或者网络带宽而并非 CPU ,既然 CPU 不是瓶颈,那么自然就采用单线程的解决方案了,况且使用多线程比较麻烦。 但是在 Redis 4.0 中开始支持多线程了,例如后台删除等功能
简单来说,Redis  4.0 之前一直采用单线程的主要原因有以下三个:

  • 使用单线程模型是 Redis 的开发和维护更简单,因为单线程模型方便开发和调试;多线程模型虽然在某些方面表现优异,但是它却引入了程序执行顺序的不确定性,带来了并发读写的一系列问题,增加了系统复杂度、同时可能存在线程切换、甚至加锁解锁、死锁造成的性能损耗。Redis通过AE事件模型以及IO多路复用等技术,处理性能非常高,因此没有必要使用多线程。单线程机制使得 Redis 内部实现的复杂度大大降低,Hash 的惰性 Rehash、Lpush 等等 “线程不安全” 的命令都可以无锁进行。

  • 即使使用单线程模型也并发的处理多客户端的请求,主要使用的是多路复用和非阻塞 IO;

  • 对于 Redis 系统来说, 主要的性能瓶颈是内存或者网络带宽而并非 CPU

3. Redis多线程模型

3.1 引入多线程原因

既然单线程那么好,为啥又要引入多线程?单线程也有自己的烦恼,比如大key删除问题: 正常情况下使用 del 指令可以很快的删除数据,而当被删除的 key 是一个非常大的对象时,例如时包含了成千上万个元素的 hash 集合时,那么 del 指令就会造成 Redis 主线程卡顿。

因此,在 Redis 4.0 中就新增了多线程的模块,当然此版本中的多线程主要是为了解决删除数据效率比较低的问题。可以通过惰性删除有效避免Redis卡顿问题(大key删除等问题),步骤如下:

  • unlink key : 与DEL一样删除key功能的lazy free实现,唯一不同是,UNLINK在删除集合类型键时,如果集合键的元素个数大于64个,主线程中只是把待删除键从数据库字典中摘除,会把真正的内存释放操作,给单独的bio来操作。如果元素个数较少(少于64个)或者是String类型,也会在主线程中直接删除。

  • flushall/flushdb async : 对于清空数据库命令flushall/flushdb,添加了async异步清理选项,使得redis在清空数据库时都是异步操作。实现逻辑是为数据库新建一个新的空的字典,把原有旧的数据库字典给后台线程来逐一删除其中的数据,释放内存。

  • 把删除工作交给了后台子进程异步删除数据

因为Redis是单个主线程处理,redis之父antirez一直强调"Lazy Redis is better Redis". 而lazy free的本质就是把某些cost(主要时间复制度,占用主线程cpu时间片)较高删除操作, 从redis主线程剥离让bio子线程来处理,极大地减少主线阻塞时间。从而减少删除导致性能和稳定性问题。

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

从上一小结分析:Redis的主要性能瓶颈是内存或网络带宽而并非CPU。内存问题比较好解决,因此Redis的瓶颈原因为网络IO。接下来,引入多线程模型。

3.2 多线程工作原理

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

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

怎么解析Redis6中的单线程和多线程模型  redis6 第3张

结合上图可知,将网络数据读写、请求协议解析通过多个IO线程的来处理,对于真正的命令执行来说,仍然使用主线程操作(线程安全),是个不错的折中办法。因此,对于整个Redis来说是多线程的,但是对于工作线程(命令执行)仍旧是单线程

3.3 工作流程

核心流程大概如下:怎么解析Redis6中的单线程和多线程模型  redis6 第4张

流程简述如下:

  • 主线程获取 socket 放入等待列表

  • 将 socket 分配给各个 IO 线程(并不会等列表满)

  • 主线程阻塞等待 IO 线程(多线程)读取 socket 完毕

  • 主线程执行命令 - 单线程(如果命令没有接收完毕,会等 IO 下次继续)

  • 主线程阻塞等待 IO 线程(多线程)将数据回写 socket 完毕(一次没写完,会等下次再写)

  • 解除绑定,清空等待队列

特点如下:

  • IO 线程要么同时在读 socket,要么同时在写,不会同时读或写

  • IO 线程只负责读写 socket 解析命令,不负责命令处理(主线程串行执行命令)

  • IO 线程数可自行配置(目前代码限制上限为 512,默认为 1(关闭此功能))

经过有心人士的压测,目前性能能提高 1 倍以上。

疑问1:等待列表不满 一直阻塞不处理吗?
回复:怎么解析Redis6中的单线程和多线程模型  redis6 第5张阻塞时检测的是,IO 线程是否还有任务。等处理完了才继续往下。这些任务是在执行时添加的,如果 任务数< 线程数,那有些线程就拿不到任务,它的待处理任务就是 0 。分配了任务的线程,在处理好 IO 事件后,pending 就会清零,没拿到任务的线程 pending 本来就是 0,所以不会阻塞。 这块还是有点疑问,哪位大佬可以解释下(评论哈)?

3.4 默认开启多线程吗?

在Redis6.0中, 多线程机制默认是关闭的 ,如果需要使用多线程功能,需要在redis.conf中完成两个设置。

  • 设置io-thread-do-reads配置项为yes,表示启动多线程。

  • 设置线程个数。关于线程数的设置,官方的建议是如果为 4 核的 CPU,建议线程数设置为 2 或 3, 如果为 8 核 CPU 建议线程数设置为 6 ,线程数一定要小于机器核数,线程数并不是越大越好。

4. 总结

Redis自身出道就是优秀,基于内存操作、数据结构简单、多路复用和非阻塞 I/O、避免了不必要的线程上下文切换等特性,在单线程的环境下依然很快;

但对于大数据的 key 删除还是卡顿厉害,因此在 Redis 4.0 引入了多线程unlink key/flushall async 等命令,主要用于 Redis 数据的异步删除;

而在 Redis 6.0 中引入了 I/O 多线程的读写,这样就可以更加高效的处理更多的任务了, Redis 只是将 I/O 读写变成了多线程 ,而 命令的执行依旧是由主线程串行执行的 ,因此在多线程下操作 Redis 不会出现线程安全的问题

Redis 无论是当初的单线程设计,还是如今与当初设计相背的多线程,目的只有一个:让 Redis 变得越来越快。

感谢你的阅读,相信你对“怎么解析Redis6中的单线程和多线程模型”这一问题有一定的了解,快去动手实践吧,如果想了解更多相关知识点,可以关注蜗牛博客网站!小编会继续为大家带来更好的文章!

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:niceseo99@gmail.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

评论

有免费节点资源,我们会通知你!加入纸飞机订阅群

×
天气预报查看日历分享网页手机扫码留言评论Telegram