面试官问,Redis 是单线程还是多线程?我懵了

云惠网小编 2022年1月15日23:17:37
评论
2582字阅读8分36秒
摘要

通常说的 Redis 是单线程,主要是指 Redis 对外提供键值存储服务的主要流程,即网络 IO 和键值对读写是由⼀个线程来完成的。

广告也精彩

Redis 在启动时,会同时启动这三个 BIO 线程,但是 BIO 线程只有在需要执行相关类型后台任务时才会唤醒,其他时间会休眠等待任务。

当有客⼾端请求和实例建立 Socket 连接时,主线程会创建和客户端的连接,并把 Socket 放入全局等待队列中。然后主线程通过轮询方法把 Socket 连接分配给 IO 线程。

需要注意的是在 Redis6.0 中,多线程机制默认是关闭的,需要在 redis.conf 中完成以下两个设置才能启用多线程。

io-threads-do-reads yes

而正是以上这些问题,才让 Redis 采⽤了单线程模式。

我们平时看到介绍 Redis 的文章,都会说 Redis 是单线程的。但是我们学习的时候,比如 Redis 的 bgsave 命令,它的作用是在后台异步保存当前数据库的数据到磁盘,那既然是异步了,肯定是由别的线程去完成的,这怎么还能说 Redis 是单线程的呢?

看到这里大家可能有点疑惑,前面说了 Redis 不是单线程,现在我们也说了 Redis 的键值对读写操作使用采用了单线程模式,那么它的其他线程是是什么样的呢?

从上面不难看出,多线程开发中并发访问控制是⼀个难点,需要精细的设计才能处理。如果只是简单地处理,比如简单地采⽤⼀个粗粒度互斥锁,只会出现不理想的结果。即便增加了线程,系统吞吐率也不会随着线程的增加而增加,因为大部分线程还在等待获取访问共享资源的互斥锁。而且,大部分采用多线程开发引入的同步原语保护共享资源的并发访问,也会降低系统代码的易调试性和可维护性。

多线程是 Redis6.0 推出的一个新特性。正如上面所说 Redis 是核心线程负责网络 IO ,命令处理以及写数据到缓冲,而随着网络硬件的性能提升,单个主线程处理⽹络请求的速度跟不上底层⽹络硬件的速度,导致网络 IO 的处理成为了 Redis 的性能瓶颈。

主进程的其它线程

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

总结

  • 设置线程个数。⼀般来说,线程个数要小于 Redis 实例所在机器的 CPU 核数, 例如,对于⼀个 8 核的机器来说,Redis 官⽅建议配置 6 个 IO 线程。

主线程执行完请求操作后,会把需要返回的结果写入缓冲区。然后,主线程会阻塞等待 IO 线程把这些结果回写到 Socket 中,并返回给客户端。等到 IO 线程回写 Socket 完毕,主线程会清空全局队列,等待客户端的后续请求。

Redis6.0 多线程

Redis 为什么用单线程?在回答这个问题前,先来看大家都很熟悉的数据库 MySQL,它使用的就是多线程。MySQL 不会每有一个连接就创建一个线程,因为线程过多会带来额外的开销,其中包括创建销毁线程的开销、调度线程的开销等,同时也会降低计算机的整体性能。这个正是多线程会遇到的难点。

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

而 Redis6.0 就是从单线程处理网络请求到多线程处理,通过多个 IO 线程并⾏处理网络操作提升实例的整体处理性能。需要注意的是对于读写命令,Redis 仍然使⽤单线程来处理,这是因为继续使⽤单线程执行命令操作,就不⽤为了保证 Lua 脚本、事务的原⼦性,额外开发多线程互斥机制了。

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

多线程流程

因此,严格地说 Redis 并不是单线程。但是我们⼀般把 Redis 称为单线程高性能,这样显得 Redis 更强一些。

全部流程分为以下 4 阶段:

io-threads 6

IO 线程解析完请求后,主线程以单线程的⽅式执⾏这些命令操作。

其实通常说的 Redis 是单线程,主要是指 Redis 对外提供键值存储服务的主要流程,即网络 IO 和键值对读写是由⼀个线程来完成的。除此外 Redis 的其他功能,比如持久化、 异步删除、集群数据同步等,是由额外的线程执⾏的。在这一点上 Node 也是一样的,一般提到 Node 也是单线程的,但其实 Node 只有一个主线程是单线程,其他异步任务则由其他线程完成。这样做的原因是防止有同步代码阻塞,导致主线程被占用后影响后续的程序代码执行。

Redis 3.0 版本后,主进程中除了主线程处理网络 IO 和命令操作外,还有 3 个辅助 BIO 线程。这 3 个 BIO 线程分别负责处理,文件关闭、AOF 缓冲数据刷新到磁盘,以及清理对象这三个任务队列,从而避免这些任务对主 IO 线程的影响。

整体流程示意图

主线程把 Socket 分配给 IO 线程后,会进⼊阻塞状态等待 IO 线程完成客户端请求读取和解析。

多进程

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

来具体看一下在 Redis6.0 中,主线程和 IO 线程是如何协作完成请求处理的。

此外多线程系统中通常会存在被多线程同时访问的共享资源,比如一个共享的数据结构,当有多个进程要修改这个共享资源时,为了保证共享资源的正确性,就需要有额外的机制进行保证,而这个额外的机制,也会带来额外的开销。还是以 MySQL 举例,MySQL 引入了锁机制来解决这个问题。

Redis 为什么用单线程

  • 收到 bgrewriteaof 命令: Redis fork 一个子进程,然后子进程往临时 AOF文件中写入重建数据库状态的所有命令。写入完毕后,子进程会通知父进程把新增的写操作追加到临时 AOF 文件。最后将临时文件替换旧的 AOF 文件,并重命名。

  • 收到 bgsave 命令: Redis 构建子进程,子进程将内存中的所有数据通过快照做一次持久化落地,写入到 RDB 中。

  • 当需要进行全量复制: master 启动一个子进程,子进程将数据库快照保存到 RDB 文件。在写完 RDB 快照文件后,master 会把 RDB 发给 slave,同时将后续新的写指令都同步给 slave。

看完了这篇文章,相信大家对 Redis 是单线程的说法已经有了大致概念。我们说它是单线程,主要是因为在以前的版本中网络 IO 和键值对读写是由⼀个线程来完成的。而之所以说 Redis 是多线程,则是因为 Redis6.0 以后的版本里,网络 IO 的部分变为了多线程处理。而且除了主线程,还有 3 个辅助 BIO 线程,分别是 fsync 线程、close 线程、清理回收线程。当然不能忘记的是,想要体验多线程机制,就得通过修改配置文件开启多线程功能。

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

除了主进程,在以下场景如果需要进行重负荷任务的处理,Redis 会 fork 一个子进程来处理:

本文转自 https://blog.csdn.net/github_36774378/article/details/122469100

腾讯云618
云惠网小编
SpringCloud -- Config、Bus解析 java

SpringCloud — Config、Bus解析

1、Config1.1、概述简介1. 分布式面临的问题微服务意味着要将单体应用中的业务拆分成一个个子服务,每个服务的粒度相对较小,因此系统中会出现大量的服务。由于每个服务都需要必要...
Java数据结构-了解复杂度 java

Java数据结构-了解复杂度

2.实例分析与计算  四.写在最后  // 计算斐波那契递归fibonacci的时间复杂度 int fibonacci(int N) { return N < 2 ? N : fibonacci...
Java数据结构-认识顺序表 java

Java数据结构-认识顺序表

目录二.顺序表1.概念及结构2.顺序表的实现打印顺序表获取顺序表的有效长度在pos位置新增元素判断是否包含某个元素查找某个元素对应的位置获取/查找pos位置的元素给pos位置的元素...
腾讯云618

发表评论