Redis到底是单线程还是多线程?
一、什么是Redis
redis是一个高性能的key-value数据库,它是完全开源免费的,而且redis是一个NOSQL类型数据库,是为了解决高并发、高扩展,大数据存储等一系列的问题而产生的数据库解决方案,是一个非关系型的数据库。但是,它也是不能替代关系型数据库,只能作为特定环境下的扩充。
redis是一个以key-value存储的数据库结构型服务器,它支持的数据结构类型包括:字符串(String)、链表(lists)、哈希表(hash)、集合(set)、有序集合(Zset)等。为了保证读取的效率,redis把数据对象都存储在内存当中,它可以支持周期性的把更新的数据写入磁盘文件中。而且它还提供了交集和并集,以及一些不同方式排序的操作。
二、Redis到底有多快
Redis采用的是基于内存的采用的是单进程单线程模型的 KV 数据库,由C语言编写,官方提供的数据是可以达到100000+的QPS(每秒内查询次数)。这个数据不比采用单进程多线程的同样基于内存的 KV 数据库 Memcached 差!有兴趣的可以参考官方的基准程序测试《How fast is Redis?》(https://redis.io/topics/benchmarks)
横轴是连接数,纵轴是QPS。 此时,这张图反映了一个数量级,希望大家在面试的时候可以正确的描述出来,不要问你的时候,你回答的数量级相差甚远!
三、Redis为什么这么快
完全基于内存,绝大部分请求是纯粹的内存操作,非常快速。数据存在内存中,类似于HashMap,HashMap的优势就是查找和操作的时间复杂度都是O(1);
数据结构简单,对数据操作也简单,Redis中的数据结构是专门进行设计的;
采用单线程,避免了不必要的上下文切换和竞争条件,也不存在多进程或者多线程导致的切换而消耗 CPU,不用去考虑各种锁的问题,不存在加锁释放锁操作,没有因为可能出现死锁而导致的性能消耗;
使用多路I/O复用模型,非阻塞IO;
使用底层模型不同,它们之间底层实现方式以及与客户端之间通信的应用协议不一样,Redis直接自己构建了VM 机制 ,因为一般的系统调用系统函数的话,会浪费一定的时间去移动和请求;
以上几点都比较好理解,下边我们针对多路 I/O 复用模型进行简单的探讨:
多路 I/O 复用模型
多路I/O复用模型是利用 select、poll、epoll 可以同时监察多个流的 I/O 事件的能力,在空闲的时候,会把当前线程阻塞掉,当有一个或多个流有 I/O 事件时,就从阻塞态中唤醒,于是程序就会轮询一遍所有的流(epoll 是只轮询那些真正发出了事件的流),并且只依次顺序的处理就绪的流,这种做法就避免了大量的无用操作。
这里“多路”指的是多个网络连接,“复用”指的是复用同一个线程。 采用多路 I/O 复用技术可以让单个线程高效的处理多个连接请求(尽量减少网络 IO 的时间消耗),且 Redis 在内存中操作数据的速度非常快,也就是说内存内的操作不会成为影响Redis性能的瓶颈,主要由以上几点造就了 Redis 具有很高的吞吐量。
四:那么为什么Redis是单线程的
原因如下:
1. CPU不是瓶颈:Redis的所有操作都是基于内存的,而CPU不是Redis的瓶颈。在大多数情况下,Redis的瓶颈很可能是机器内存或网络带宽的大小。如果我们想要更高的性能,可以使用单线程Redis,我们可以使用集群(多个进程)解决方案。
2. 并发性:并行性不是支持多个客户端的唯一策略。Redis使用epoll和事件循环来实现并发策略并节省大量时间而无需上下文切换。
3. 易于实现:编写多线程程序可能会更加困难。我们需要为线程添加锁和同步机制。
4. 易于部署:单线程应用程序可以部署在至少具有单个CPU内核的任何计算机上。
并发与并行?
并发性和并行性之间的区别
1. 并发就是一次处理很多事情。并行是关于一次做很多事情。
2. 并发是关于结构;并行是关于执行的。
3. 并发提供了一种构造解决方案的方法,以解决可能(但不一定)可并行化的问题。
我们可以使用餐厅服务员的类比:
什么是并发
服务员可以为多个客户提供服务,而一次只能为一个客户准备菜。
由于厨房提供的菜肴之间会有一定的间隔,因此当顾客人数少于5人时,一位侍者通常可以处理。
什么是并行
假设厨房一次可以为20位顾客提供餐具。如果一位服务员的顾客数量太大,我们需要更多的服务员。在这种情况下,多个服务员同时工作。我们称其为并行性。
五:多线程的Redis?
在 2019 年 12 月 20 号这天,众所期待的 Redis 新版 6.0 rc1 发布了(Redis 6 RC1 is out today),肯定很多关注的同学都进行了试用,虽然因为引入了 c11 的 _Atomic 导致相当多的环境都无法直接编译成功,但是对于想一探究竟的粉丝们来说,这是完全阻挡不了的热情,
新版除了增加 ACLS 权限控制模块、支持更为广泛的新协议 RESP3、客户端缓存、无磁盘同步、Cluster Proxy 等十来个相当实用的新特性外,还对 长久以来 社区筒子们 呼声比较高的 多线程 进行了支持,当然也带来 性能提升了一倍 的好处
这次我们的任务有两个: - 剖析 Redis6 多线程的实现方式 - 与 Memcached 的多线程模型(个人认为这是一个极其经典的多线程网络编程案例)进行对比
加入多线程 IO 之后,整体的读流程如下:
- 主线程负责接收建连请求,读事件到来(收到请求)则放到一个全局等待读处理队列
- 主线程处理完读事件之后,通过 RR(Round Robin) 将这些连接分配给这些 IO 线程,然后主线程忙等待(spinlock 的效果)状态
- IO 线程将请求数据读取并解析完成(这里只是读数据和解析并不执行)
- 主线程执行所有命令并清空整个请求等待读处理队列(执行部分串行)
上面的这个过程是完全无锁的,因为在 IO 线程处理的时主线程会等待全部的 IO 线程完成,所以不会出现 data race 的场景。
参考链接:https://www.sohu.com/a/331991216_268033