Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >细品Redis高性能数据结构之hash对象

细品Redis高性能数据结构之hash对象

作者头像
袁新栋-jeff.yuan
发布于 2020-08-26 01:56:16
发布于 2020-08-26 01:56:16
86800
代码可运行
举报
运行总次数:0
代码可运行

背景

上一节讲Redis的高性能字符串结构SDS,今天我们来看一下redis的hash对象。

Hash对象

简介

  • redis的hash对象有两种编码(底层实现)方式,字典编码和压缩列表编码。在使用字典编码的时候程序就是将hash表的key存为字典的键,hash的value作为字典的值,字典的键值都是用的是字符串类型。
  • 在哈希对象保存的所有键值对的键和值的字符串长度都小于 64 字节和哈希对象保存的键值对数量小于 512 个使用的是ziplist,不能满足这个的使用的是hashtable(字典编码)

深度理解

ZipList(压缩列表)
  1. redis 的压缩列表是一块连续的内存空间,元素之间紧挨着存储,没有任何冗余空间
  2. 源码
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct ziplist<T> {
int32 zlbytes; // 整个压缩列表占用字节数
int32 zltail_offset; // 最后一个元素距离压缩列表起始位置的偏移量,用于快速定位到最后一个
节点
int16 zllength; // 元素个数
T[] entries; // 元素内容列表,挨个挨个紧凑存储
int8 zlend; // 标志压缩列表的结束,值恒为 0xFF
}


/**
*entry对象源码
*/
struct entry {
int<var> prevlen; // 前一个 entry 的字节长度
int<var> encoding; // 元素类型编码
optional byte[] content; // 元素内容
}
  1. 压缩列表是支持双向遍历,所以才会有zltail_offset这个字段的,可以进行快速定位到最后一个元素。然后倒序查找(O(1))
  2. prevlen 表示的是前一个字段的长度,有人就有疑问了,为什么是前一个entry的长度,为什么不是自己的呢,其实他还有一个作用是在压缩列表倒叙遍历的时候,需要通过这个字段来快速定位到下一个元素的位置,由于他是一个连续的存储空间,已经知道当前元素的位置+这个空间地址就可以确定写一个entry的位置。为什么会这样呢?因为entry的大小是不一样的。如果是一样的话就可以根据下表进行行为(个人理解,有错误还请指出),且prevlen 是一个变长的整数,redis的常规操作,将不同长度使用不同的数据类型。节省内存
  3. encoding的意思是元素的编码类型,有了这个字段就可以决定元素内容的设定,内存大小的分配。防止内存分配浪费的一种方式。具体内容查看下面
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
1、00xxxxxx 最大长度位 63 的短字符串,后面的 6 个位存储字符串的位数,剩余的字
节就是字符串的内容。
2、01xxxxxx xxxxxxxx 中等长度的字符串,后面 14 个位来表示字符串的长度,剩余的
字节就是字符串的内容。
310000000 aaaaaaaa bbbbbbbb cccccccc dddddddd 特大字符串,需要使用额外 4 个字节
来表示长度。第一个字节前缀是 10,剩余 6 位没有使用,统一置为零。后面跟着字符串内
容。不过这样的大字符串是没有机会使用的,压缩列表通常只是用来存储小数据的。
411000000 表示 int16,后跟两个字节表示整数。
511010000 表示 int32,后跟四个字节表示整数。
611100000 表示 int64,后跟八个字节表示整数。
711110000 表示 int24,后跟三个字节表示整数。
811111110 表示 int8,后跟一个字节表示整数。
911111111 表示 ziplist 的结束,也就是 zlend 的值 0xFF10、1111xxxx 表示极小整数,xxxx 的范围只能是 (0001~1101), 也就是 1~13,因为
000011101111 都被占用了。读取到的 value 需要将 xxxx 减 1,也就是整数 0~12 就是
最终的 value。
  1. 之前有讲到hash对像选用压缩列表的两个前提条件,其中之一是键值的大小都小于64,具体为什么小于64和简=键值对小于512就不具体说了,可以结合一下SDS中的扩容方式思考一下,压缩列表没有冗余空间,在进行扩容的时候会出现频繁扩容,再加上占用空间大了后进行copy数据就很浪费性能了。所以当数据量大了后,就选择了另一种数据结构那就是hashtable(字典)

HashTable(字典)

简介
  • redis 的hashtable和java中的hashMap实现方式是类似的,都是通过数组和链表实现的。也就是key-value形式。当然它解决hash冲突的方式也是使用链地址法(解决hash冲突的几种方法可以想一下),当不同的key创建出了相同的hash值时将vlue就放入链表上,如下图。
  • 在细节方面和java中的hashMap差别还是很大的。列如扩容的过程,key值得hash算法等等。接下来我们根据源码细细的品一品。
  • 官方给的解释:字典(dictionary), 又名映射(map)或关联数组(associative array), 是一种抽象数据结构, 由一集键值对(key-value pairs)组成, 各个键值对的键各不相同, 程序可以添加新的键值对到字典中, 或者基于键进行查找、更新或删除等操作

其字典的底层结构是使用的是redis 中dict。不仅是hash对象底层使用了dict,而且在redis全局也是使用的是key-vlue结构,也就是字典的形式,还有Zset的数据结构底层也是基于redis 中的dict结构。我们来看一下其源码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// resdis 全局使用的字典结构
struct RedisDb {
dict* dict; // all keys key=>value
dict* expires; // all expired keys key=>long(timestamp)
...}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// 有序集合的底层数据结构
struct zset {
dict *dict; // all values value=>score
zskiplist *zsl;
}
2. dict结构深度解析
  • 源码:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
/*
 * 字典
 *
 * 每个字典使用两个哈希表,用于实现渐进式 rehash
 */
typedef struct dict {

    // 特定于类型的处理函数
    dictType *type;

    // 类型处理函数的私有数据
    void *privdata;

    // 哈希表(2 个)
    dictht ht[2];

    // 记录 rehash 进度的标志,值为 -1 表示 rehash 未进行
    int rehashidx;

    // 当前正在运作的安全迭代器数量
    int iterators;

} dict;
  • 可以类的成员变量中看到有两个hashtable,通常情况下是一个有值一个没有值。在压缩列表中我们遇到的问题是在扩容方面存在性能问题,这两个hashtable就是来解决扩容问题的。在扩容和缩容时进行渐进式搬迁,当搬迁结束的时候将旧的hashtable进行删除,新的hashtable 取而代之。
  • 那我们来细细的研究一下hashtable,(Java中的hashtable是Java中hashMap的线程安全版本)。在这里的hashtable和java中的hashmap是类似的,解决hash冲突的方式通过分桶的方式。一维数组,二维链表。但是在扩容还是有一些区别的。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
struct dictEntry {
void* key;
void* val;
dictEntry* next; // 链接下一个 entry
}
struct dictht {
dictEntry** table; // 二维
long size; // 第一维数组的长度
long used; // hash 表中的元素个数
...
}
  • 来看一下redis中hash是如何进行的 1.大字典的扩容是非常耗时间的,需要重新申请新的数组,然后将旧的字典所有的链表中的元素重新挂接到新的数组下面,这个过程时间复杂度为O(n),作为单线程的redis怎么会把时间浪费在这里呢,。于是他就采用了渐进式处理的方式(说到渐进式是否能想到他渐进式批量根据key查询呢scan 和 keys), rehash的过程点击这里。其思想也就是我们上面所说的小步执行。
  • 联系一下Set结构也是通过字典实现的,只不是所有的value都是NULL,有没有想到什么?Java中的hashSet是不是也和这个类似呢?。

总结

  1. hash对象有两种底层实现方式,hashtable(字典) 和 ziplist(压缩链表)
  2. 压缩链表由于是连续空间在刚开始数据量小的时候性能是显著的,但是在数据量大的时候就会出现扩容慢的问题
  3. 字典通过双hahstable的方式,再加上渐进式hash的方式解决了压缩列表的扩容的问题
  4. redis 高性能数据结构我们可以看到他在很对细节的把握很多,如不同的数字大小选用不同的字段类型,同一个对象根据大小选择不同的存储类型。(节省内存)
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/08/12 ,如有侵权请联系 cloudcommunity@tencent.com 删除

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

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

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

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Redis技术知识总结之一——Redis 的数据结构
Redis 常用的数据类型主要有:String, List, Hash, Set, ZSet 五种,它们分别对应的底层数据结构有:
剑影啸清寒
2020/07/09
8600
Redis底层数据结构
由图中可知,底层的数据结构有所变化,在Redis7中不再推荐使用ziplist,而是使用listpack代替,但考虑兼容性,目前仍保留ziplist。
用户3876103
2024/08/26
1330
Redis学习笔记(二)redis 底层数据结构
在上一节提到的图中,我们知道,可以通过 redisObject 对象的 type 和 encoding 属性。可以决定Redis 主要的底层数据结构:SDS、QuickList、ZipList、HashTable、IntSet、ZskipList 。
归思君
2023/10/16
3000
Redis学习笔记(二)redis 底层数据结构
一文读懂 Redis 常见对象类型的底层数据结构
Redis 是一个基于内存中的数据结构存储系统,可以用作数据库、缓存和消息中间件。Redis 支持五种常见对象类型:字符串(String)、哈希(Hash)、列表(List)、集合(Set)以及有序集合(Zset),我们在日常工作中也会经常使用它们。知其然,更要知其所以然,本文将会带你读懂这五种常见对象类型的底层数据结构。
肉眼品世界
2020/11/11
8490
一文读懂 Redis 常见对象类型的底层数据结构
《redis设计与实现》1-数据结构与对象篇
学习完《redis设计与实现》前面关于数据结构与对象的章节,以上问题都能得到解答。你也能了解到redis作者如此的煞费苦心设计了这么多丰富的数据结构,目的就是优化内存。学完这些内容,在使用redis的过程中,也会合理的使用以适应它内部的特点。当然新版本的redis支持了更多更丰富的特性,该书基于redis3版本,还没有涉及到那些内容。
kinnylee
2020/10/15
5830
《redis设计与实现》1-数据结构与对象篇
Redis 基础数据结构
Redis用到的底层数据结构有:简单动态字符串、双端链表、字典、压缩列表、整数集合、跳跃表等,Redis并没有直接使用这些数据结构来实现键值对数据库,而是基于这些基础数据结构创建了一个对象系统,这写对象包括字符串对象、列表对象、哈希对象、集合对象和有序集合对象等。
luoxn28
2019/11/06
1.3K0
一文理解Redis底层数据结构
Redis的5种常见数据结构:字符串(String)、列表(List)、散列(Hash)、集合(Set)、有序集合(Sorted Set)。这些都是Redis对外暴露的数据结构,本文将介绍这些数据结构的底层数据结构的实现。
全菜工程师小辉
2021/06/25
1.3K0
一文理解Redis底层数据结构
万字长文,38 图爆肝 Redis 基础!
Redis 在互联网技术存储方面的使用可以说是非常广泛了,只要是接触过 Java 开发的朋友就算你没用过,都会听过它。在面试也是非常高频的一个知识点。
JavaFish
2021/04/29
6050
万字长文,38 图爆肝 Redis 基础!
Redis原理篇之数据结构
redis中保存的Key是字符串,value大多也是字符串或字符串集合,因此字符串是Redis中最常使用的一种数据结构。
大忽悠爱学习
2022/05/28
1.1K0
Redis原理篇之数据结构
Redis对象底层数据结构实现概述
Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic string,SDS)的抽象类型,并将SDS用作Redis的默认字符串表示。
kentian
2019/03/05
1.1K0
Redis对象底层数据结构实现概述
数据结构与对象
简单动态字符串(simple dynamic string,SDS),结构体非常简单
用户7962184
2020/11/20
8110
数据结构与对象
Redis源码阅读(二)底层数据结构
Redis对于底层数据结构的极致封装,是Redis高效运行的原因之一。我们结合Redis源码对其进行分析。
星沉
2022/01/28
9210
REDIS 数据结构与对象
Redis 是一种非关系类型数据库,以(k, v)的形式储存数据信息。由于读写速度很快,常被应用于缓存方向。Redis 使用对象来代表数据库中的键和值。Redis 有 5 种数据对象,分别是字符串对象( String 对象)、列表对象( List )、哈希对象( Hash )、集合对象、以及有序集合对象(Sorted Set)五种。其 key 值的形式都是使用的字符串形式,value 的形式可以是上面五种对象中的任意一种。Redis 对象内存结构如图1所示:
政采云前端团队
2023/09/27
2390
REDIS 数据结构与对象
Redis对象底层数据结构实现概述
| 导语 本文是一篇redis读书笔记,主要内容整理自 Redis设计与实现。如果你想快速了解redis底层数据结构,相信这篇文章会有所帮助。 文章主要分为两大部分,第一部分介绍了Redis对象的各种底层数据结构,第二部分总结了redis对象与各种底层数据结构的关系。 1  Redis对象底层数据结构 1.1  SDS(简单动态字符串)  Redis没有直接使用C语言传统的字符串表示(以空字符结尾的字符数组,以下简称C字符串),而是自己构建了一种名为简单动态字符串(simple dynamic strin
腾讯Bugly
2019/05/16
1.9K1
Redis对象底层数据结构实现概述
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
还记得Redis五种数据类型、String、List、Set、Hash、ZSet吗?如果忘记可以到这里重新温习:Redis基础(超详解)一 :Redis定义、SQL与NoSQL区别、Redis常用命令、Redis五种数据类型、String、List、Set、Hash、ZSet;Redis的Java客户端
寻求出路的程序媛
2024/11/03
1960
Redis常见面试题:ZSet底层数据结构,SDS、压缩列表ZipList、跳表SkipList
Redis 数据结构与对象编码 (Object Encoding)
为了将性能优化到极致,redis 作者为每种数据结构提供了不同的实现方式,以适应特定应用场景。
烂猪皮
2020/11/10
6970
Redis 底层数据结构概述(v6.2)
Redis(Remote Dictionary Server ),即远程字典服务,是一个使用 ANSI C 编写的开源、支持网络、基于内存、分布式、可选持久性的键值对(key-value) 数据库,与 Memcached 类似,却优于 Memcached。
恋喵大鲤鱼
2022/06/19
4240
Redis 底层数据结构概述(v6.2)
Redis详解(四)------ redis的底层数据结构
  上一篇博客我们介绍了 redis的五大数据类型详细用法,但是在 Redis 中,这几种数据类型底层是由什么数据结构构造的呢?本篇博客我们就来详细介绍Redis中五大数据类型的底层实现。
IT可乐
2018/07/31
8360
Redis详解(四)------ redis的底层数据结构
为了拿捏 Redis 数据结构,我画了 40 张图(完整版)
前几天发了一篇「为了拿捏 Redis 数据结构,我画了 20 张图」,收获了很多好评,但是当时急于发文,有些地方没有写完,也有些地方写的不是很完善。
小林coding
2021/12/02
4230
为了拿捏 Redis 数据结构,我画了 40 张图(完整版)
Redis 为何这么快?聊聊它的数据结构~
Redis是一个由ANSI C语言编写,性能优秀、支持网络、可持久化的K-K内存数据库,并提供多种语言的API。它常用的类型主要是 String、List、Hash、Set、ZSet 这5种
芋道源码
2019/03/08
6680
Redis 为何这么快?聊聊它的数据结构~
相关推荐
Redis技术知识总结之一——Redis 的数据结构
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验