Redis数据类型及编码格式——Hash篇

2023-11-10

写在前面

以下的知识都是建立在目前线上稳定版6.2.6版本的

而目前github上的redis源代码又更新优化了许多,譬如:hash数据类型默认的编码格式已经被替换为listpack,hashtable数据结构也被重新优化(哈希表对象dictht被废弃,字典dict直接引用dictEntry节点)

Redis hash数据类型-编码格式

redis中hash数据类型使用了两种编码格式:ziplist(压缩列表)、hashtable(哈希表)

具体使用哪种编码格式,由redis根据情况自己选择更合适的编码格式,这对于上层用户完全透明

  • ziplist,又称为压缩列表,是hash数据类型默认使用的编码格式,当满足某种条件后,redis会将ziplist优化为hashtable编码格式,我们也可通过配置来更改达到条件的阈值
    hash-max-ziplist-entries 512  //配置当field-value超过512时(合起来1024),使用hashtable编码
                                  //至于为什么是1024,与ziplist有关,后面会讲述
    hash-max-ziplist-value 64  //配置当key的单个field或value长度超过64时,使用hashtable编码
  • hashtable,又称为哈希表,是hash数据类型中的数据过多或多长时由ziplist优化而来,底层数据结构与ziplist完全不同,且数据结构不可逆(也就是某个key的数据从ziplist演变为hashtable后,就算其数据减少到了ziplist可接受范围,也不会再从hashtable还原为ziplist格式)

ziplist

ziplist(压缩列表)是一个经过特殊编码的双向链表,用于存储字符串或整数,可以提高存储效率。

与普通双向链表的对比

  • 内存开销不同,普通双向链表的元素一般需要两个指针,这会占用额外的内存,在数据较小的情况下,可能指针对于内存的占用比数据还大,有点得不偿失。而ziplist压缩列表,是一块连续的内存,数据之间紧密相连,不需要指针这种额外的开销。
  • 遍历速度不同,普通链表内的元素一般是分开的,通过指针来查找到下一个元素。而ziplist是在一块连续内存上,所以内部元素遍历起来更快,并且由于ziplist独特的数据结构,它存储了末尾元素的偏移位置,所以它可以轻松定位到末尾元素,所以它获取首尾元素时所需的时间复杂度为O(1)。

ziplist的缺点

  • 元素变动,虽然ziplist可以有效提高存储效率,可是这种数据项紧密相连的存储结构不擅长做修改操作,一旦元素发生变动,极大概率会引发内存重新分配造成数据拷贝,从而降低性能
  • 数据项过多,当它数据项过多时,遍历查找指定元素也会耗时更久,所以这种数据结构也不宜存储过多的元素

因此,虽然redis精心设计了ziplist这种存储效率很高的数据结构,但是只会在适宜的场景下使用,当数据项过多或元素过长时,redis会将ziplist编码格式优化为更加合适的编码格式

ziplist数据结构

ziplist主要由下图几个部分组成,各部分所占字节如图所示:

字节存储的是16进制数据,最大值为0XFF,也就是255,而类似zlen字段占用两个字节,所能表述的最大值也就是FF FF,也就是2^16 - 1,也就是65535,当小于这个值时,zlen表示entry数据项的个数,当这个值为最大值时,zlen已经不能表示entry数据项的大小了,只能通过遍历可知。

entry数据项数据结构

entry数据项主要由下图几个部分构成:

我们来对其进行简单介绍:

1.首先是prevlen,它存储的是前一个数据项entry的长度,占用1或5个字节,可依据此字段进行反向遍历

  • 如果长度在0XFE也就是254之内,则prevlen占用1个字节。
  • 如果长度大于等于254,则prevlen占用5个字节。此时,第一个字节值设为0XFE(254),意味着使用后面更大的值,而剩余的4个字节用来表示前一个数据项的长度。(注:至于为什么不使用0XFF,也就是255,是因为255作为ziplist的结尾符,如果在遍历读取entry时,由于entry本身长度就不固定,那么此时就可能将其误认为结尾符,从而引发错误)

2.其次是encoding,这个字段相对复杂,它根据entry-data存入的是整数还是字符串会有不同的格式,所占的字节数也不相同,一般通过第一个字节就可确定数据项是整数还是字符串,整数的话,第一个字节的前2bit是11,否则为字符串

如果为字符串时,占用字节不固定

  • 字符串长度小于64时,占用1个字节,二进制结构为00pppppp,后6bit表示长度,最高可表示2^6 - 1,也就是63
  • 字符串长度小于16384时,占用2个字节,二进制结构为01pppppp qqqqqqqq,后14bit表示长度,最高可表示2^14 - 1,也就是16283
  • 字符串长度大于等于16384时,占用5个字节,二进制结构为10000000 qqqqqqqq rrrrrrrr ssssssss tttttttt,第一个字节后6bit不使用,后面4个字节的32bit位用来表示长度,最高可表示2^32 - 1,也就是4294967295

如果为整数时,都只占1字节,前2bit确定是否整数节点,后6bit确定整数节点的类型

  • 二进制结构为11000000,值为0XC0,entry-data存储int16_t类型数据(占用2字节)
  • 二进制结构为11010000,值为0XD0,entry-data存储int32_t类型数据(占用4字节)
  • 二进制结构为11100000,值为0XE0,entry-data存储int64_t类型数据(占用8字节)
  • 二进制结构为11110000,值为0XF0,entry-data存储3个字节的整数
  • 二进制结构为11111110,值为0XFE,entry-data存储1个字节的整数
  • 二进制结构为1111xxxx,这是特殊情况,此时xxxx的值在0001至1101之间(因为0000和1110都已经被上面使用了),所以xxxx换算为十进制也就是1-13,而数值应该从0开始,所以得剪1,代表的就是0-12这几个值。此时xxxx直接用来表示entry-data的数据值,也就是不存在entry-data了

3.最后是entry-data,存储整数或字节数组。

  • 当存入的是字符串时,会存入每个字符在ASCII表中对应的16进制值,然后形成一个字节数组。
  • 当存入的是数字时,如果是上面所讲的特殊情况(即encoding为1111xxxx这种),那么该字段不存在,否则会按所属情况存储不同类型的数据,数据类型不同所占用字节也不同

ziplist数据结构实例分析

例如:逐步分析下图中的ziplist字节数据

  1.  zlbytes,4个字节,由于redis采用是小端模式存储,所以实际应为00 00 00 1a,转化为十进制是26,表示该ziplist总共有26个字节
  2. zltail,4个字节,00 00 00 0c转化为十进制是12&#
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Redis数据类型及编码格式——Hash篇 的相关文章

  • StackExchange.Redis 和 StackExchange.Redis.StrongName 之间有什么区别?

    当我关注Azure时文档 http azure microsoft com en us documentation articles cache dotnet how to use azure redis cache 关于如何在Azure
  • 使用 sidekiq 处理两个独立的 Redis 实例?

    下午好 我有两个独立但相关的应用程序 他们都应该有自己的后台队列 阅读 单独的 Sidekiq 和 Redis 进程 然而 我希望偶尔能够将工作推给app2的队列来自app1 从简单的队列 推送的角度来看 如果app1没有现有的 Sidek
  • 如何清理redis中不活跃的玩家?

    我正在制作一个使用 redis 来存储游戏状态的游戏 它可以很好地跟踪位置和玩家 但我没有一个好的方法来清理不活跃的玩家 每当玩家移动时 这是一个半慢速移动游戏 想想每秒 1 5 帧 我就会用新位置更新哈希并删除旧位置键 跟踪活跃玩家的最佳
  • Docker&Celery - 错误:Pidfile (celerybeat.pid) 已存在

    应用程序包括 姜戈 雷迪斯 芹菜 码头工人 Postgres 在将项目合并到 docker 之前 一切都运行顺利且正常 但是一旦将其移入容器 就开始出现问题 起初它开始得很好 但过了一会儿我确实收到了以下错误 celery beat 1 E
  • connect-redis - 如何保护会话对象免受竞争条件影响

    我使用 nodejs 和 connect redis 来存储会话数据 我将用户数据保存在会话中 并在会话生命周期中使用它 我注意到两个更改会话数据的请求之间可能存在竞争条件 我尝试过使用 redis lock 来锁定会话 但这对我来说有点问
  • Node.js 上通过套接字连接 Redis

    由于共享托管 目标主机上的我的 redis 服务器不在端口上运行 而是在非常特定的套接字上运行 可以通过套接字文件连接到该套接字 只有我的用户可以访问 但是 我还没有找到如何通过套接字指定连接node redis and connect r
  • 如何使用Spring Cache处理redis异常?

    我目前正在开发一个包含 Spring Data Redis 和 Spring Cache 的项目 在spring data redis中 我使用redis模板调用redis 我在 try catch 块中处理 redis 模板抛出的所有异常
  • 如何统计 Redis 流中未读或已确认的消息?

    使用 Redis 5 0 3 假设我们创建一个名为streamy和一个消费群体consumers XGROUP CREATE streamy consumers MKSTREAM 然后向其中添加一些消息 XADD streamy messa
  • 如果另一个键中的计数器低于零,则从集合中原子删除一个项目?

    雷迪斯2 0 3 在我的 Redis DB 中 我有一组项目 每个项目都有一个与其关联的计数器 MULTI SADD items set foo INCRBY items foo 10000 EXEC 新项目会以随机间隔添加到集合中 当用户
  • redis-cli 重定向到 127.0.0.1

    我在PC1上启动Redis集群 然后在PC2上连接它 当需要重定向到另一个集群节点时 它会显示Redirected to slot 7785 located at 127 0 0 1 但应该显示Redirected to slot 7785
  • 通过 StackExchange.Redis 连接到 Redis Servier

    我尝试使用以下方法制作一个测试项目Redis https redis io服务器 通过 Virtual Box 安装在 Linux Ubuntu 虚拟机上 Linux 机器通过 Virtual Box 的桥接适配器与本地网络连接 Virtu
  • Redis、会话过期和反向查找

    我目前正在构建一个网络应用程序 并想使用 Redis 来存储会话 登录时 会话会使用相应的用户 ID 插入到 Redis 中 并且过期时间设置为 15 分钟 我现在想实现会话的反向查找 获取具有特定用户 ID 的会话 这里的问题是 由于我无
  • redis - 使用哈希

    我正在使用 redis 为我的 Web 应用程序实现社交流和通知系统 我是 redis 的新手 我对哈希值及其效率有一些疑问 我读过这篇很棒的文章Instagram 帖子 http instagram engineering tumblr
  • 有没有办法让特定的key在集群模式下定位到特定的redis实例上?

    我想让我的多锁位于不同的redis实例上 我发现redission可以指定一个实例来执行命令 但是如果该命令与key相关 则指定的实例会将命令传输到另一个实例 你能给我一些建议吗 你可以 但这并不是微不足道的 首先 Redis 在键中使用大
  • 使用 Celery 通过 Gevent 进行实时、同步的外部 API 查询

    我正在开发一个 Web 应用程序 该应用程序将接收用户的请求 并且必须调用许多外部 API 来编写对该请求的答案 这可以直接从主 Web 线程使用 gevent 之类的东西来扇出请求来完成 或者 我在想 我可以将传入的请求放入队列中 并使用
  • StackExchange.Redis的正确使用方法

    这个想法是使用更少的连接和更好的性能 连接会随时过期吗 对于另一个问题 redis GetDatabase 打开新连接 private static ConnectionMultiplexer redis private static ID
  • 如何使 Redis 缓存中数据层次结构(树)的部分内容无效

    我有一些产品数据 需要在 Redis 缓存中存储多个版本 数据由 JSON 序列化对象组成 获取普通 基本 数据的过程很昂贵 将其定制为不同版本的过程也很昂贵 因此我想缓存所有版本以尽可能进行优化 数据结构看起来像这样 BaseProduc
  • StackExchange.Redis Get 函数抛出 TimeoutException

    我在用着StackExchange Redis与 C 和StackExchangeRedisCacheClient Get函数抛出以下异常 myCacheClient Database StringGet txtKey Text myCac
  • 在 Redis 上为 Django 和 Express.js 应用程序共享会话存储

    我想创建一个包含一些登录用户的 Django 应用程序 另一方面 由于我想要一些实时功能 所以我想使用 Express js 应用程序 现在的问题是 我不希望身份不明的用户访问 Express js 应用程序的日期 因此 我必须在 Expr
  • Redis+Docker+Django - 错误 111 连接被拒绝

    我正在尝试使用 Redis 作为使用 Docker Compose 的 Django 项目的 Celery 代理 我无法弄清楚我到底做错了什么 但尽管控制台日志消息告诉我 Redis 正在运行并接受连接 事实上 当我这样做时 docker

随机推荐

  • Linux保护文件实现,Linux完整性保护机制模块实现分析(1)

    原标题 Linux完整性保护机制模块实现分析 1 2 详细分析2 1 模块功能描述 文件系统完整性模块包含四种机制 监控磁盘机制 同步机制 检查修复文件系统机制 监视文件系统机制 1 监控磁盘机制主要由statfs by dentry vf
  • Maven安装(超详解)

    2 4 1 下载 下载地址 Maven Download Apache Maven 在提供的资料中 已经提供了下载好的安装包 如下 2 4 2 安装步骤 Maven安装配置步骤 解压安装 配置仓库 配置Maven环境变量 1 解压 apac
  • 生成csv

    package com study csv import java io File import java io FileNotFoundException import java io FileOutputStream import ja
  • python 行转列与气泡图,echarts玫瑰图画图

    原数据 stack data stack to frame head 10 unstack data pd read excel Users bella Desktop 考研 xlsx dropna data data set index
  • 经典坐标变换案例代码剖析

    题目 设有小萝卜一号和小萝卜二号位于世界坐标系中 记世界坐标系为W 小萝卜们的坐标系为R1和 R2 小萝卜一号的位姿为q2 0 35 0 2 0 3 0 1 T t1 0 3 0 1 0 1 T 小萝卜二号的位姿为q2 0 5 0 4 0
  • 镜像下载网站(全网最全)

    几家企业提供的镜像站 阿里云开源镜像 http mirrors aliyun com 搜狐开源镜像 http mirrors sohu com 网易开源镜像 http mirrors 163 com LUPA http mirror lup
  • Vue的详细教程--基础语法【上】

    Welcome Huihui s Code World 接下来看看由辉辉所写的关于Vue的相关操作吧 目录 Welcome Huihui s Code World 一 插值 1 文本 2 html 3 属性 class绑定 style绑定
  • 西门子200SMART(四) 程序块

    程序块是显示当前项目包含的程序列表 一般初始状态共有三个 主程序 子程序和中断程序 如下图 当然 鼠标右键选择某一个程序块 可以重命名 支持中文 然而一个项目中 只能有一个主程序 也必须有一个主程序 所以主程序是无法删除的 在初始状态下 想
  • 用pm2在本地部署服务器node项目,全栈实用技能,pm2部署node应用到服务器

    好东西就要拿来分享 不管你的目标是前端还是全栈 都值得一看 背景介绍 一般的 我们开发一个前端项目通常是在本地通过Node js搭一个服务器 所有的开发测试过程基本上都是在本地搞定 有时候 我们需要把我们的作品上线 好让更多的人能够访问到
  • 某网站hexin-v的解决方法,hexin-v的解密方法,hexin-v的生成方法

    前段时间做的项目 一直运行比较稳定 最近几天运行过程中异常退出 结果发现是某网站接口有变 使用谷歌chrome 对原网站进行调试 结果发现某个js页面有变化 以原有的hexin v生成方法 已经不能适用于最新的算法 本想偷偷懒从网络上找一下
  • Map和Set

    Map和Set是集合中的两个接口 Set实现了Collection接口 而Map没有实现 Map下面又有很多子类 我们主要研究HashMap和TreeMap Set同样有很多子类 主要研究HashSet和TreeSet 在理解掌握它们之前
  • FTP命令使用实例

    ftp命令是标准的文件传输协议的用户接口 ftp是在TCP IP网络上的计算机之间传输文件的简单有效的方法 它允许用户传输ASCII文件和二进制文件 在ftp会话过程中 用户可以通过使用ftp客户程序连接到另一台计算机上 从此 用户可以在目
  • LR.net敏捷软件开发平台核心功能详解

    软件开发 程序员就是不断地跟变量 方法 类 接口这些东西打交道 随着开发经验的积累 很多程序员会发现 虽然最终开发出来的软件每个都不一样 但是在开发过程中用到的很多东西却又是相通的 例如 每个软件的底层差不多都需要进行增删改查 文件操作 权
  • State 模式

    有限状态机 FSM Finite state machine 例子 1 若状态机在Locked状态收到了一个coin事件 则迁移到Unlocked状态并执行unlock动作 2 若状态机在UnLocked状态收到了一个pass事件 则迁移到
  • 基于Spark MLlib平台的协同过滤算法---电影推荐系统

    说到推荐系统 大家可能立马会想到协同过滤算法 本文基于Spark MLlib平台实现一个向用户推荐电影的简单应用 其中 主要包括三部分内容 协同过滤算法概述 基于模型的协同过滤应用 电影推荐 实时推荐架构分析 一 协同过滤算法概述 本人对算
  • npm安装Electron解决方案

    npm安装Electron解决方案 Electron使用npm安装时 因为是国外的镜像源 所以速度会非常慢 而使用cnpm如下命令进行安装时 又会出现安装失败的问题 npm install electron g execute post i
  • 第一阶段-第四章 Python的循环语句

    目录 前言 1 循环语句的运行方式 2 为什么要学习循环语句 3 本章的安排 一 while循环的基础语法 1 学习目标 2 while循环语句 3 本节的演示 4 while循环的注意点 5 本小节的总结 6 课后练习 二 while循环
  • 【JavaEE初阶】第三节.多线程基础篇

    作者简介 大家好 我是未央 博客首页 未央 303 系列专栏 JavaEE初阶 每日一句 人的一生 可以有所作为的时机只有一次 那就是现在 目录 文章目录 前言 一 认识线程 二 多线程程序 2 1 第一个Java多线程程序 2 2 怎么样
  • 6.1 什么是面向对象以及类和对象的区别

    什么是面向对象 面向对象 面向对象编程的简称 Object Oriented Programming OOP 是一种对现实世界理解和抽象的方法 是计算机编程技术发展到一定阶段后的产物 早期的计算机编程是面向过程的 典型的代表是 C 语言 解
  • Redis数据类型及编码格式——Hash篇

    写在前面 以下的知识都是建立在目前线上稳定版6 2 6版本的 而目前github上的redis源代码又更新优化了许多 譬如 hash数据类型默认的编码格式已经被替换为listpack hashtable数据结构也被重新优化 哈希表对象dic