前言
redis是一个单线程模型,是一个非常重要的缓存方式。其底层数据十分的基本,但效果确实出奇好,这致使很多大公司开始对redis有更加深刻的研究。
提示:以下是本篇文章正文内容,下面案例可供参考
一、底层数据结构优化
底层数据结构优化主要有SDS、跳跃表、字典、压缩列表、整数集合、快速列表、enconding转化(这部分是机器自己转化的无需深究)。
1 SDS
SDS(Sample Dynamic String):可修改的动态字符串。SDS本质可以理解为char*,因为表头sdshdr结构的存在,sds比c更加优秀,也可以兼容c字符串
给大家看下底层结构
struct sdshdr{
//使用数量
int len;
//未使用数量
int free;
//保存字符串
int buf[];
}
在这里面buf[]是c中的动态数组
长度的存储:我们可以快速读出他的长度,由于有free的位置所以不会造成溢出;
二进制存储:redis可以进行二进制存储,结束的时候可以用len进行控制;
2 跳跃表
为什么会有跳跃表,首先我们要清楚链表的数据结构,每当我们进行增删改操作的时候,需要的时间复杂度为O(1),但我们要查找一个元素的时间复杂度为O(n),如果使用这种方式将大大的影响到效率,那么就需要找到一种空间换时间的方式来解决问题。由此一种类似于二分查找算法思想的跳跃表就出现了。
如图所示:在原链表的情况下重新添加几个新的链表其key值相同,这样的话我们的时间复杂度就可以降到O(logn),但是空间复杂度会变成原先的两倍左右。
留个思考:我们可不可以在原始的链表上多添加几条指针来达到相同的效果。在每个节点处都多编译几条指针,这样我们既可以保证原空间复杂度不变,又可以节约相当大部分时间复杂度。
3字典
首先我们要知道redis数据存在数组中,数组的特点是连续的并且随机访问(可以降低时间复杂度可以参考快排优化)。
Redis用的是Hash算法,非固定长将key转成一个整数->%len->余数就是数组的下标
当余数相同时会发生hash冲突
方法一 开放地址法
方法二 拉链法,向下起单链表
Dictht ht[2]有两张表ht[0] ht[1]
当ht[0]不够用的时候会扩充到ht[1],其大小时ht[0]的两倍。Ht[0]会向ht[1]进行拷贝。这种拷贝方式叫rehash。渐进式hash在轻松的时候扩的比较快,否则比较慢。
Dicttype是一个多态的操作(因为存储的的对象是object)
字典存储的优势
存储的时间复杂度为O(1)
使用数组支持海量数据处理
4 压缩列表
压缩列表(ziplist)是列表键和哈希键的底层实现之一。它是经过特殊编码的双向链表,和整数集合(intset)一样,是为了提高内存的存储效率而设计的。当保存的对象是小整数值,或者是长度较短的字符串,那么redis就会使用压缩列表来作为哈希键的实现。
压缩列表的特点
压缩列表ziplist结构本身就是一个连续的内存块,由表头、若干个entry节点和压缩列表尾部标识符zlend组成,通过一系列编码规则,提高内存的利用率,使用于存储整数和短字符串。
压缩列表ziplist结构的缺点是:每次插入或删除一个元素时,都需要进行频繁的调用realloc()函数进行内存的扩展或减小,然后进行数据”搬移”,甚至可能引发连锁更新,造成严重效率的损失
详细参阅:https://blog.csdn.net/qq_20853741/article/details/111946054
5 快速列表
快速列表是一个由ziplist组成的双向链表。quicklist的特点:
quicklist宏观上是一个双向链表,因此,它具有一个双向链表的有点,进行插入或删除操作时非常方便,虽然复杂度为O(n),但是不需要内存的复制,提高了效率,而且访问两端元素复杂度为O(1)。
quicklist微观上是一片片entry节点,每一片entry节点内存连续且顺序存储,可以通过二分查找以 log2(n)log2(n) 的复杂度进行定位。
小结
redis底层都是我么们熟知和简单的数据结构,但是其一起使用却能快速的达到读取的目的。接下来会对其模型单线程如何实现快速操作进行解释。
二、Redis单线程模型
1、Reactor
Redis事件处理机制采用单线的Reactor模式,属于I/O的多路复用的一种模式
2、Epoll模型
t提到Epoll模型就不得不提到Select模型
Socket里面维护了一个等待队列,A过来了开始等待将其放入等待队列,A被挂起,那么BC就可以处理了。
1、 来一个就得阻塞一次
2、 每来一次数据就需要遍历一次就绪态数据。
支持的最长socket是1024
Epoll的优化
先用epoll_create创建一个Epoll对象Epfd,再通过epoll_ctl将需要监视的Socket添加到Epfd中,最后调用epoll_wait等待数据。
3、Evenloop
Evenloop是驱动核心管理着文件事件列表和时间事件列表,不断地循环处理着就绪文件事件和事件事件列表不断处理着就绪文件事件和到期文件事件。如果是搞前台的一定要仔细研究。
总结
Redis为什么快, 因为其在内存中,redis中没有锁的概念