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

111次阅读
没有评论

共计 3660 个字符,预计需要花费 10 分钟才能阅读完成。

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

1. Redis 演进史

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

版本 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 调用,而这个拷贝的过程就是阻塞的,当数据量越大时拷贝所需要的时间就越多,而这些操作都是基于单线程完成的。

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

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

3.3 工作流程

核心流程大概如下:

流程简述如下:

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

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

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

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

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

解除绑定,清空等待队列

特点如下:

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

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

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

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

疑问 1:等待列表不满 一直阻塞不处理吗?
回复:阻塞时检测的是,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 中的单线程和多线程模型”这一问题有一定的了解,快去动手实践吧,如果想了解更多相关知识点,可以关注丸趣 TV 网站!丸趣 TV 小编会继续为大家带来更好的文章!

正文完
 
丸趣
版权声明:本站原创文章,由 丸趣 2023-07-15发表,共计3660字。
转载说明:除特殊说明外本站除技术相关以外文章皆由网络搜集发布,转载请注明出处。
评论(没有评论)