redis中数据结构的学习笔记

redis中数据结构的学习笔记


redis中,对于一个键值对的值,可以有多种数据结构,比如字符串,集合,数组。如果要细分的话,主要分为以下这几种结构:

  1. 简单动态字符串
  2. 双向链表
  3. 压缩列表
  4. 哈希表
  5. 跳表
  6. 整数数组

对于键和值redis用什么数据结构进行组织呢?
在redis中,采用全局的哈希表组织键值对。全局哈希表中每个哈希桶存储了k和v的指针。
在这里我们可以想到一个潜在的风险点:当在 Redis 中写入大量数据后,就可能发现操作有时候会突然变慢了。这里有一个潜在的风险点,那就是哈希表的冲突问题和 rehash 可能带来的操作阻塞。

redis中采用拉链法来解决冲突问题:
大概过程就是:

  1. 有2个全局哈希表,先从hash表1中存放键值对,而hash表2不分配空间
  2. 当插入数据变的越来越多,拉链法的处理方式导致桶里的链表过长,时间性能下降
  3. 这时就会开始rehash。给表2分配更大的空间,并把表1中的数据重新映射拷贝到表2中,然后释放表1的空间
    1. 这里有个问题,如果数据量很大,那么这个拷贝的过程非常耗时,会造成线程的阻塞,无法服务其他请求
    2. 那么这里redis采用的渐进式rehash
    3. 简单来说就是在拷贝数据时,Redis 仍然正常处理客户端请求,每处理一个请求时,从哈希表 1 中的第一个索引位置开始,顺带着将这个索引位置上的所有 entries 拷贝到哈希表 2 中;等处理下一个请求时,再顺带拷贝哈希表 1 中的下一个索引位置的 entries。

压缩列表

压缩列表实际上类似于一个数组,数组中的每一个元素都对应保存一个数据。和数组不同的是,压缩列表在表头有三个字段 zlbytes、zltail 和 zllen,分别表示列表长度、列表尾的偏移量和列表中的 entry 个数;压缩列表在表尾还有一个 zlend,表示列表结束。
在压缩列表中,如果我们要查找定位第一个元素和最后一个元素,可以通过表头三个字段的长度直接定位,复杂度是 O(1)。而查找其他元素时,就没有这么高效了,只能逐个查找,此时的复杂度就是 O(N) 了。

跳表

跳表在链表的基础上,增加了多级索引,通过索引位置的几个跳转,实现数据的快速定位。
实现跳表的一个关键因素是有序,底层链表必须是有序的。
在数据量较大的情况下,跳表查找的时间复杂度是logN。

问题辨析

整数数组和压缩列表在查找时间复杂度方面并没有很大的优势,那为什么 Redis 还会把它们作为底层数据结构呢?

  1. 内存利用率,数组和压缩列表都是非常紧凑的数据结构,它比链表占用的内存要更少。Redis是内存数据库,大量数据存到内存中,此时需要做尽可能的优化,提高内存的利用率。
  2. 数组对CPU高速缓存支持更友好,所以Redis在设计时,集合数据元素较少情况下,默认采用内存紧凑排列的方式存储,同时利用CPU高速缓存不会降低访问速度。当数据元素超过设定阈值后,避免查询时间复杂度太高,转为哈希和跳表数据结构存储,保证查询效率。

依据局部性原理,每次尝试访问主内存,操作系统一次访问的单位是一个 缓存行 的大小(64 字节),如果这一次将64字节的数据全缓存到缓存行中了,下一次访问的是缓存行64字节数据之外的数据,那么缓存未命中导致缓存行失效。那么就会再访问主存,然后再将这一次访问的新的64字节存储到缓存行。
所以说,随机访问对高速缓存还友好吗?

Redis底层的使用数组和压缩链表对数据大小限制在64个字节以下(这个64字节,通常也就是cpu缓存行的大小),当大于64个字节会改变存储数据的数据结构,所以随机访问对于CPU高速缓存没啥影响。
就是说,当数据在64字节以内的时候,随机访问总是可以命中缓存行。当大于64字节,虽然对高速缓存不友好了,但是会通过改变数据结构来改善效率。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值