前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Redis 基础知识

Redis 基础知识

作者头像
少羽大怪兽
发布2020-12-03 10:44:17
3180
发布2020-12-03 10:44:17
举报
文章被收录于专栏:架构技术架构技术

数据类型

常用的包括**String、List、Hash、Set、Sorted Set**,不常用的包含GEO、Bitmap、HyperLogLog;底层数据结构包括简单字符串,双向链表,数组,压缩数组,哈希表,跳表;数据类型跟数据结构的对应关系为下图所示;

img
img

hash算法

  • 一个哈希表就是一个数组,数组中的每个元素称为一个哈希桶(Bucket);
  • 采用hash算法进行键值对的存储;
  • 一个键值对对应一个entry,entry里包含键和 值的指针,并不存储实际的值;如下图所示:
img
img
  • 查找值时,先通过键的值计算出hash值,再通过hash值以哈希表数组的长度取模,就能得到查找的键的entry对应的下标;例如,假设hash表数组的长度是1000,那么在查找键key所在的位置时,只需要计算key值的hash值,假设为12425475,那么,key值所在的位置的下表即为12425475%1000=475,可直接通过下表获取到key对应的entry。

hash冲突与渐进式rehash

  • 什么是hash冲突?

  hash表数组长度是固定的,当使用hash算法当存储的数据量远远大于hash表的数组长度时,就必定会有键计算出来的数组下标相同。假设hash表数组长度为10,key1的hash值为15,key2的hash值为25,那么他们与10取模后都是5,即key1和key2都要存储在下标为5的地方,这就是hash冲突。

  • 如何解决hash冲突?

  当发生hash冲突时,redis采用单向链表的方式存储hash值,即在entry中增加next指针,指向下一个元素,比如上面的情况,key1已经存储在了5的位置,那么key2存储时,会更新key1的next指针,指向key2。同样是以上情况,如果key1和key2已经存储了,需要查找key2的值,则通过hash取模后得到的位置是5,再进行对比key1的值,如果不等于key2,那么就通过key1的next指针,继续对比下一个,直到找到为止。具体的数据结构图下图所示。

img
img
  • 以上解决办法带来的问题?

  从上面的情况我们可以得知,当hash冲突时,需要通过链表一个个对比才能查找到我们想要的key。如果是比较极端的情况,在某个hash桶位置的冲突键太多,就容易导致查找效率低下,这就是hash冲突带来的问题。

  • 如何解决?

  redis通过渐进式rehash来解决以上带来的问题的。rehash操作就是增加现有桶的数量,让逐渐增多的entry元素在更多的桶之间分散保存,从而减少单个桶的冲突。我们可以先看一下redis存储的一些源码:

代码语言:javascript
复制
typedef struct dictEntry {
void *key; //键
union {
void *val; //值
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next; //指向下一个节点,形成链表
} dictEntry;

typedef struct dictht {
dictEntry **table; //哈希表数组,数组的每个项是dictEntry链表的头结点指针
unsigned long size; //哈希表大小;在redis的实现中,size也是触发扩容的阈值
unsigned long sizemask;//哈希表大小掩码,用于计算索引值;总是等于 size-1 
unsigned long used;//哈希表中保存的节点的数量
} dictht;

typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];//在字典内部,维护了两张哈希表。 一般情况下, 字典只使用 ht[0] 哈希表, ht[1] 哈希表只会在对 ht[0] 哈希表进行 rehash 时使用
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
} dict;

触发条件

1. 存储元素与hash表长度的比例大于某个值(负载因子); 2. 单个桶存储的元素大于某个值;

如何扩容

Redis 默认使用了两个全局哈希表:哈希表 1 和哈希表 2。一开始,当你刚插入数据时,默认使用哈希表 1,此时的哈希表 2 并没有被分配空间。随着数据逐步增多,Redis 开始执行 rehash,这个过程分为三步:

1. 给哈希表 2 分配更大的空间,例如是当前哈希表 1 大小的两倍; 2. 把哈希表 1 中的数据重新映射并拷贝到哈希表 2 中; 3. 释放哈希表 1 的空间。

渐进式rehash

以上扩容过程会带来一个问题,当数据量较多时,这个过程比较耗时,那么就必定会阻塞redis处理请求,所以redis采用渐进式rehash来解决这个问题,简单来说,Redis 仍然正常处理客户端请求,每处理一个请求时,从哈希表 1 中的第一个索引位置开始,顺带着将这个索引位置上的所有 entries 拷贝到哈希表 2 中;等处理下一个请求时,再顺带拷贝哈希表 1 中的下一个索引位置的 entries。如下图所示:

img
img
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-12-01 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档