1. Redis单线程为什么效率还这么高?
这里先强调一点:redis单线程指的是网络请求模块使用了一个线程(所以不需考虑并发安全性),即一个线程处理所有网络请求,其他模块仍用了多个线程。
客户端与服务端建立连接交由socket,可以同时建立多个连接(这里应该是多线程/多进程),建立的连接redis是知道的(为什么知道,去看socket编程,再次强调基础很重要),然后redis会基于这些建立的连接去探测哪个连接已经接收完了客户端的请求数据(注意:不是探测哪个连接建立好了,而是探测哪个接收完了请求数据),而且这里的探测动作就是单线程的开始,一旦探测到则基于接收到的数据开始数据处理阶段,然后返回数据,再继续探测下一个已经接收完请求数据的网络连接。
注意:从 探测 到 数据处理 再到 数据返回,全程单线程。这应该就是所谓的redis单线程。
先来说说单线程和多线程吧!
两个任务按两种方式执行:
- 方式一:一个线程执行任务A,另一个线程执行任务B
- 方式二:一个线程先执行任务A,执行完以后继续执行任务B
首先两个任务如果单独执行,时间是相同的。
方式一除了这个时间外,主要耗时在CPU线程切换上,有个上下文切换时间,这个是很耗时的。这样看貌似多线程更费时间,其实并不是。当任务是I/O操作(例如磁盘I/O、网络I/O等)时,多线程的优势就出来了。
多线程的优势在哪呢?
在于I/O操作上,更具体的说是在等待I/O阶段。I/O操作分为两个阶段:等待I/O准备就绪、真正操作I/O。在I/O等待这段时间中,磁盘在找要读的数据,这里就有寻道耗时和旋转耗时(寻道:磁头在磁道间反复移动;旋转:盘面旋转将数据所在扇区移动至读写头(磁头)下),这段时间中线程是阻塞的,CPU核心是空闲的,此时就可将该CPU核心用于其他线程,效率也就提高了。
现在说回Redis
Redis是读写数据都在内存中进行,并没有涉及到I/O操作(Redis的性能和CPU无关)自然也就选择单线程啦。
那Redis的性能瓶颈在哪呢?
1.机器内存:决定了能够存储的数据量
2.网络带宽:决定了能够接受命令的数量(客户端和服务端就近部署)
Redis客户端执行一条命令分为四个过程:发送命令、命令排队、命令执行、返回结果
其中,发送命令+返回结果这一过程被称为Round Trip Time(RTT,往返时间)
Redis的客户端和服务端可能部署在不同的机器上。例如客户端在北京,服务端在上海,两地直线距离约为1300公里,那么1次RTT时间=1300×2/(300000×2/3)=13毫秒(光在真空中传输速度为每秒30万公里,这里假设光纤为光速的2/3),那么客户端在1秒内大约只能执行80次左右的命令,这就和Redis的高并发高吞吐特性背道而驰!所以一般情况下,都是就近部署!
2.Redis为什么这么快
(1) 绝大部分请求是纯粹的内存操作(非常快速)
(2) 采用单线程,避免了不必要的上下文切换和竞争条件
(3) 非阻塞IO - IO多路复用,Redis采用epoll
做为I/O多路复用技术的实现,再加上Redis自身的事件处理模型将epoll中的连接,读写,关闭都转换为了时间,不在I/O上浪费过多的时间。
3. Redis zset 为什么使用skiplist而不是B+树?
对于这个问题,Redis的作者 @antirez 是怎么说的:
There are a few reasons:
- They are not very memory intensive. It’s up to you basically. Changing parameters about the probability of a node to have a given number of levels will make then less memory intensive than btrees.
- A sorted set is often target of many ZRANGE or ZREVRANGE operations, that is, traversing the skip list as a linked list. With this operation the cache locality of skip lists is at least as good as with other kind of balanced trees.
- They are simpler to implement, debug, and so forth. For instance thanks to the skip list simplicity I received a patch (already in Redis master) with augmented skip lists implementing ZRANK in O(log(N)). It required little changes to the code.
这里从skiplist的内存占用、对范围查找的支持和实现难易程度这三方面总结的原因。
内存占用
skiplist比平衡树更灵活一些。一般来说,平衡树每个节点包含2个指针(分别指向左右子树),而skiplist每个节点包含的指针数目平均为1/(1-p),具体取决于参数p的大小。如果像Redis里的实现一样,取p=1/4,那么平均每个节点包含1.33个指针,比平衡树更有优势。
B+树的原理是:叶子节点存储数据,非叶子节点存储索引。
B+树的每个节点可以存储多个关键字,它将节点大小设置为磁盘页的大小,充分利用了磁盘预读的功能。每次读取磁盘页时就会读取一整个节点,每个叶子节点还有指向前后节点的指针,为的是最大限度的降低磁盘IO(数据在 内存中读取 耗费的时间是从 磁盘IO读取 的百万分之一)。而Redis是内存中读取数据,并不涉及I/O;
实现
平衡树的插入和删除操作可能引发子树的调整,逻辑复杂,而skiplist的插入和删除只需要修改相邻节点的指针,操作简单又快速。
查询
查找单个key,skiplist和B+树的时间复杂度都为O(log n),范围查找也同样是找到第一个满足条件的数据后直接往后读取满足条件数据即可。
4. mysql为什么选择使用B+树而不是skiplist
skiplist并没有磁盘预读功能,适用于内存。
B+树按页预读,针对磁盘I/O更高效
欢迎大家评论,如果本文对您有帮助,请点个赞,您的点赞对我很重要!这次一定!感谢!!!
转发请注明出处呦!感谢!!!