我们平时看到介绍Redis的文章,都说Redis是单线程的。但是我们在学习的时候,比如Redis的bgsave命令,它的作用是在后台将当前数据库的数据异步保存到磁盘中。既然是异步的,就必须由其他线程来完成。怎么能说Redis是单线程呢?其实通常所说的Redis是单线程的,主要是指Redis对外提供key-value存储服务的主进程,即网络IO和key-value对读写是由一个线程。此外,Redis的其他功能,如持久化、异步删除、集群数据同步等,都是由额外的线程来完成的。节点在这一点上是一样的。一般提到Node也是单线程的,但实际上Node只有一个主线程是单线程的,其他的异步任务都是由其他线程完成的。这样做的原因是为了防止同步代码阻塞,导致主线程被占用,影响后续程序代码的执行。所以严格来说Redis不是单线程的。但是我们一般将Redis称为单线程高性能,这使得Redis更加强大。为什么Redis使用单线程?为什么Redis使用单线程?在回答这个问题之前,我们先来看看熟悉的数据库MySQL,它使用的是多线程。MySQL不会每次有连接就创建一个线程,因为太多的线程会带来额外的开销,包括创建和销毁线程的开销,调度线程的开销等等,也会降低计算机的整体性能.这正是多线程会遇到的难点。另外,在多线程系统中,通常会有多个线程同时访问的共享资源,比如共享数据结构。当多个进程要修改这个共享资源时,为了保证共享资源的正确性,需要额外的机制。保证,而这种额外的机制也会带来额外的开销。还是以MySQL为例,MySQL引入了锁机制来解决这个问题。从上面不难看出,多线程开发中的并发访问控制是一个难点,需要精心设计来处理。如果只是简单的处理,比如简单的使用粗粒度的互斥锁,只会有不尽如人意的结果。即使增加了线程,系统吞吐率也不会随着线程的增加而增加,因为大多数线程仍然在等待获取互斥锁来访问共享资源。而且多线程开发引入的同步原语大多保护共享资源的并发访问,也会降低系统代码的可调试性和可维护性。正是以上问题,使得Redis采用了单线程模式。看到这里,你可能有点懵了。我们前面说过Redis不是单线程的。现在我们也说了Redis的键值对读写操作采用单线程模式。它的其他线程呢?主进程的其他线程Redis3.0版本之后,除了处理网络IO和命令操作的主线程外,主进程中还有3个辅助BIO线程。这三个BIO线程分别负责处理、关闭文件、刷新AOF缓冲数据到磁盘、清理对象三个任务队列,避免这些任务对主IO线程的影响。当Redis启动时,这三个BIO线程会同时启动,但是BIO线程只有在需要执行相关类型的后台任务时才会被唤醒,其他时间会休眠等待任务。Redis除了主进程外,在以下场景会fork一个子进程来处理重任务:接收bgrewriteaof命令:Redisfork一个子进程,然后子进程写入临时AOF文件重建所有命令的数据库状态。写入完成后,子进程会通知父进程将新的写入操作追加到临时AOF文件中。最后用临时文件替换旧的AOF文件并重命名。收到bgsave命令:Redis建立子进程,子进程通过快照将内存中的所有数据持久化登陆并写入RDB。当需要全量拷贝时:master启动一个子进程,子进程将数据库快照保存到rdb文件中。写入RDB快照文件后,master会将RDB发送给slave,同时将后续所有新的写命令同步给slave。Redis6.0多线程多线程是Redis6.0引入的新特性。上文提到,Redis是负责网络IO、命令处理、向缓冲区写入数据的核心线程。随着网络硬件性能的提升,单个主线程处理网络请求的速度跟不上底层网络硬件的速度,导致网络IO的处理成为Redis的性能瓶颈。而Redis6.0是从单线程处理网络请求到多线程处理,通过多个IO线程并行处理网络操作来提高实例的整体处理性能。需要注意的是,Redis仍然使用单线程来处理读写命令。这是因为继续使用单线程执行命令操作,不需要额外开发多线程互斥来保证Lua脚本和事务的原子性。机制。需要注意的是,在Redis6.0中,多线程机制默认是关闭的,开启多线程需要在redis.conf中完成以下两个设置。将io-thread-do-reads配置项设置为yes,开启多线程。io-threads-do-readsyes设置线程数。一般来说,线程数小于Redis实例所在机器的CPU核数。比如8核的机器,Redis官方建议配置6个IO线程。io-threads6多线程进程看Redis6.0中主线程和IO线程是如何配合完成请求处理的。整个过程分为以下四个阶段:第一阶段:服务器端和客户端建立Socket连接,分配处理线程。进入全局等待队列。然后主线程通过轮询的方式将Socket连接分配给IO线程。Phase2:IO线程读取并解析请求。主线程将Socket分配给IO线程后,会进入阻塞状态,等待IO线程完成客户端请求的读取和解析。第三阶段:主线程执行请求操作。IO线程解析请求后,主线程以单线程的方式执行这些命令操作。阶段4:IO线程回写Socket,主线程清空全局队列。主线程执行完请求操作后,会将需要返回的结果写入缓冲区。然后,主线程会阻塞等待IO线程将这些结果写回Socket返回给客户端。IO线程回写Socket后,主线程会清空全局队列,等待客户端后续请求。总结看完这篇文章,相信大家对Redis是单线程的说法有了一个大概的了解。我们说它是单线程的,主要是因为在以前的版本中,网络IO和键值对的读写都是一个线程完成的。Redis之所以是多线程的,是因为在Redis6.0之后的版本中,网络IO的部分变成了多线程。并且除了主线程外,还有3个BIO辅助线程,分别是fsync线程、close线程和清理回收线程。当然,你不能忘记,想要体验多线程机制,还得通过修改配置文件来开启多线程功能。推荐阅读原创内容屡遭盗用?从源头上向资源盗用说NO,严重危害警告!Log4j执行漏洞公开!
