本文以收录专栏 redis核心技术
前言
本专栏为了帮助大家更好的了解学习redis,同时也是自己记录学习redis的内容,包含了大部分的redis核心技术,分布式锁,主从复制等
目录
1.1redis为什么快
它在接收到一个键值对操作之后,能以微秒级别的速度找到数据。
为什么能做到这么快呢
- 它是内存数据库,所有操作都在内存上完成
- 归功于redist的数据结构。
1.2redis数据结构的底层实现
除了String 其他都是集合类型
1.3键和值用什么结构组织
redis使用一个hashb表保存所以的键值对。
但是哈希桶中的元素保存的不是值本身,而是指向具体值的指针。无论是String还是集合类型,哈希桶中元素都是指向它们的指针。
那集合类型如何存储?
entry保存了*key和*value指针,分别指向了实际的键和值。
用一个哈希表保存所有的键值对,这样做的好处是通过O(1)时间复杂度快速查找到键值对-》我们只需要计算键的哈希值,就可以知道它对应的哈希桶的位置,然后访问相应的entry元素。
但是存在潜在风险导致操作变慢:
- 哈希冲突
- rehash可能带来操作阻塞
1.3.1哈希冲突
当两个key的哈希值和哈希桶计算对应关系是,正好落在同一个哈希桶中。
redis如何解决这种哈希冲突。-》同一个哈希桶的多个元素用一个链表来保存,它们之间用指针连接。
当冲突越多,链表变长,就会导致在这个链上的元素查找耗时长,效率降低。
1.3.2rehash操作
为了防止哈希冲突链表过长,就会对hash表进行rehash操作-》也就是增加哈希桶数量,让逐渐增多的entry元素能在更多的桶之间分散保存。
如何做到?
使用两个全局哈希表,默认只使用一个哈希表,另外一个哈希表不分配空间。当数据增多,redis就会开始执行rehash。
而原来的哈市hash表作为下一次rehash扩容备用。
有什么问题?
涉及大量的数据拷贝,如果一次性把哈希表迁移完全,会造成线程阻塞,无法服务其他请求。
如何解决?
渐进式rehash。
在拷贝数据的时候,redis正常处理客户端请求,每处理一个请求,从hash表1中第一个索引位置,顺带着将这个索引位置上的所有entries拷贝到哈希表2中,等处理下一个请求时,在顺带拷贝哈希表1中的下一个索引位置的entries。
1.4集合数据
压缩列表:三个字段 zlbytes列表长度,zltail列表尾的偏移量,zllen列表中的entry个数。
跳表:增加了多级索引,通过索引位置的几个跳转,实现数据的快速定位。不与普通列表一般,只能逐一查找数据
调表的查找复杂度是
1.4.1数据结构的时间复杂度
1.4.2不同操作的复杂度
- 单元素操作 每一种集合对单个数据实现的增删改查操作 O(1)‘
但是集合支持多个元素进行增删改查,这时这些操作的复杂度由单个元素操作复杂度和元素个数决定。如HMSET增加M个元素,复杂度从O(1)变成O(M)
- 范围操作 集合类型中的遍历操作,可以返回集合中所有的数据。这类操作复杂度一般是O(N),比较耗时
但是redis从2.8版本开始提供了SCAN系列操作,每次只返回有限数量的数据,例如HGetAll,Smembers避免一次性返回所有元素造成redis阻塞
- 统计操作 集合类型对集合中所有元素个数的记录
- 例外情况 压缩列表和双向链表都会记录表头和表尾的偏移量