redis到底是多线程还是单线程?
首先我们需要知道的,redis处理用户请求一直都是单线程,只是redis4(包含redis4)之后才引入了多线程的概念,比如后台线程的RDB保存io线程、aof后台io线程、异步删除、集群同步等这些都是后台线程,也就是说redis整体来说是多线程的(redis4),而redis的用户请求则是单线程的。
那为什么redis4之前乃至现在用户请求还是单线程?
首先是因为单线程的架构比较简单,方便调试和开发,其次就是redis的性能其实主要是和内存和网络有关,和cpu的性能其实没多大关系,另外就是redis使用的非阻塞IO和IO多路复用也可以在单线程的架构下解决多客户端的并发需求。
redis为什么那么快?
首先就是他是一个内存数据库,没有频繁的磁盘IO,另外就是设计的数据结构让每次操作都能在时间复杂度O(1),还有就是上面说到的多路复用和非阻塞IO,让一个线程来完成多客户端的请求,没有频繁的线程切换而导致的性能损失,以及单线程的架构让各个客户端之间不存在锁竞争和死锁造成的性能问题。主要原因就是多路复用+epoll函数+内存+多网络IO才是redis为什么这么快的直接原因。
那为什么又要在redis4之后使用多线程?
首先就是随着工业的发展多核心多线程已经开始普及,所以没必要再墨守成规只使用单线程,当然这里的单线程我指的是redis整体,其次就是redis4之前的大Key删除缓慢的问题,虽然读写的速度非常快,但是在删除一个有成千上万个成员的key时时间就会下降了,这个时候其他客户端是被阻塞的状态,如果频繁出现这种情况那对整个服务来说还是十分恐怖的性能影响,所以引入了异步删除,当然我们得使用对应的异步删除命令如下对比:
| 删除操作 | 同步/异步 | 阻塞情况 | 适用场景 | 命令示例 |
|---|---|---|---|---|
| DEL | 同步阻塞 | 阻塞当前客户端 | 小数据量,立即需要结果 | DEL key |
| UNLINK | 异步非阻塞 | 不阻塞当前客户端 | 大数据量,性能敏感 | UNLINK key |
| FLUSHDB async | 异步非阻塞 | 不阻塞当前客户端 | 清空整个数据库 | FLUSHDB ASYNC |
| FLUSHDB | 同步阻塞 | 阻塞所有客户端 | 需要立即结果的小数据库 | FLUSHDB |
| FLUSHALL async | 异步非阻塞 | 不阻塞当前客户端 | 清空所有数据库 | FLUSHALL ASYNC |
redis性能瓶颈之网络IO
我们都知道redis的主要性能瓶颈就是内存和网络,内存我们可以增加redis的配置来使用更多的内存,而网络呢?这个我们不能单纯的改配置就行了,这个需要改架构,也就是redis6引出的多网络IO线程,之前的版本一个线程IO处理多个客户端网络请求,那么越多的客户端这个网络IO的线程压力是不是就越大?但是再大一个线程也只能吃我们网络硬件的一部分性能,也就是一个网络IO无法充分榨干网络硬件的性能。所以提出了多线程网络IO,用多线程来解决不断激增的客户端网络请求,这样redis的网络瓶颈就被打破了,真正做到了机器有多好性能就有多好的性能。但是我们redis的读写还是单线程的,只是处理网络请求变成了多线程IO,既解决了网络瓶颈的问题,也不用考虑读写的事务、锁竞争的问题。
那这个网络IO是干什么的?单线程和多线程有什么区别?
首先这个网络IO就是解析客户端连接的信息、命令的,只有把客户端的socket连接解析正确了,才能知道这个客户端在哪里、要干什么,之前单线程的就是并发执行的,也就是A解析比如10毫秒,B连接解析10毫秒,不停的切换,只有解析好了主线程也就是我们的读写线程才能通过多路复用epoll的方式去处理客户端的请求,那假如这个IO线程要解析的东西太多了,是不是就没办法最快把所有客户端的请求解析完,那么这个时候的主线程是不是没办法最大功率执行,因为在你主线程执行完了上一个客户请求,下一个用户请求还没解析完。另外如果像之前一个网络IO,主线程执行完需要把数据返回客户端,也是需要网络IO进行解析的,那这个时候网络IO又要解析连接又要打包返回数据,实在是有点太忙了。所以多网络IO就解决了这个问题,一个人解析肯定比多人干活要慢的,这不就解决了这个问题吗。
redis单线程和多线程的执行流程?
单线程模式:用户发来连接请求,主线程监听到了马上让网络IO去解析,解析完成后执行命令,主线程执行命令,然后通知网络IO打包数据返回客户端。如果客户端请求过多就会堵塞,因为读和写本事就是阻塞的。
多线程模式:100个用户发来连接请求,主线程监听到了使用多路复用epoll将这些请求全部外包给多线程网络IO解析,然后主线程继续监听,只要监听到网络IO解析完成,主线程马上执行命令,执行完成后主线程又通过多路复用把需要打包的数据外包给了网络IO去执行,他继续接待其他请求的命令。
注意点:多路复用和非阻塞IO(网络IO)redis一直都是这个架构,只是redis6推出了网络IO多线程。另外redis6/7都是默认关闭网络IO多线程的,如果要使用需要去配置文件中修改。
总结
本篇主要讲的就是redis多线程和单线程之间的区别。