Redis KV存储结构
Redis属于内存数据库的一种,采用KV键值对的方式存储。在数据结构上,便采用了哈希表作为存储,存放key的值和对应的value的地址。使在查询中,得到O(1)的时间复杂度。哈希表中存放的是value的地址,并不是value数据本身
哈希冲突
既然使用了哈希表存储,那一定会存在哈希冲突,Redis采用了链表的方式来解决,类似于java中HashMap的方式。
java中的ThreadLocal中使用了开放寻址发解决哈希冲突
渐进式扩容
那还有一个问题在于,当存储的元素数量达到阈值时,Hash表效率开始下降,速度变慢了。那么需要对Hash表进行扩容。redis在扩容的过程中,主要分为辅助扩容和定时扩容。
Redis的这个扩容简称为rehash,reids底层有2个数组,可以理解为有2个哈希表。ht[1]和ht[0]。假设目前ht[1]正在使用,redis需要扩容:
- ht[0] 变成2倍ht[1]长度
- 当有请求进来时,先在ht[1]中执行命令,没有找到数据后,再到ht[0]中执行命令
- 当有命令进来是,辅助式扩容,把在ht[1]中命中的索引位置上吗的数据,全部带到ht[0]中。其中新增命令只在ht[0]中执行。
- 当redis处于空闲是,定时库容,reids自动检测,把ht[1]上的数据搬运过去,防止在redis中长时间同事使用ht[1]和ht[0]2个
- 当完成数据转移后,ht[1]清除内存空间,redis只在ht[0]操作
优点:避免了一次大的数据copy,分化到每次小的copy中 缺点是:在扩容是需要占用大量的内存空间,本身已经达到内存瓶颈的redis上面,会造成无法扩容的问题
Redis 数据类型
- String
- Map
- Set
- Sorted Set
- List
Redis支持如上5中数据类型的value。 redis在不同的数据类型上采用不同的数据结构来存储,达到速度快的目的
- 简单动态字符串
- 双向链表
- 压缩列表
- 哈希表
- 跳表
- 整数数组
大致上更具目的,把这些分为2类,一类是为了减少内存碎片,提高内存的使用率和CPU缓存的使用率,如简单动态字符串 压缩链表 整数数组。
压缩链表:头部有3个字段zlbytes(链表长度),zltail(链表的尾部的偏移量),zllen(列表中内存的个数),和尾部的zlend
这样的好处是,虽然列表中间的一些元素查找需要O(n),但是对于头部和尾部的元素操作,只需要O(1)的复杂度,大大提升了效率,这也是在redis中pop push等操作快速的原因。
为了提高查询的速度,使用了跳表,压缩链表,哈希表,双向链表。
哈希表的时间复杂度为O(1),跳表如下
如图可见在有序的一串key中,可以快速查询到对应的key,时间为O(logN)
redis 在数据结构上做了这些方法,来提升他的速度,在网络的io模型上面,在于多路复用IO模型,就是NIO,select/epoll模型。这里就不做赘述了,关于NIO等的io模型好处网上大把大吧的文章。