浅析 Redis 中 String 数据类型及其底层编码

news2024/12/26 15:37:27

从 RedisObject 说起

在 Redis 中,任意数据类型的键和值都会被封装为一个 RedisObject ,也叫做Redis对象,源码如下

 

c

复制代码

/*server.h*/ typedef struct redisObject { unsigned type:4; unsigned encoding:4; unsigned lru:LRU_BITS; /* LRU time (relative to global lru_clock) or * LFU data (least significant 8 bits frequency * and most significant 16 bits access time). */ int refcount; void *ptr; } robj;

我们来看一下这个结构体中的成员变量分别代表什么:

  • unsigned type:4 :对象类型,分别是 string hash list set zset ,占 4 个 bit 位,如下所示

     c 

    复制代码

    #define OBJ_STRING 0 /* String object. */ #define OBJ_LIST 1 /* List object. */ #define OBJ_SET 2 /* Set object. */ #define OBJ_ZSET 3 /* Sorted set object. */ #define OBJ_HASH 4 /* Hash object. */
  • unsigned encoding:4: 底层编码方式,共有 11 种,4 个 bit 位

  • unsigned lru:LRU_BITS :该对象最后一次被访问的时间,占 24 个 bit ,在 Redis 内存回收中起到关键作用

  • int refcount :对象引用计数器,计数器为 0 则说明对象无人引用,可以被回收

  • void *ptr:指针,指向存放实际数据的空间

我们注意到,在 Redis 中有 5 中数据结构(用户使用的),但在底层却有 11 种编码方式,Redis 会根据存储的数据类型、存储数据的大小,选择不同的编码方式,以获得最优的性能。一种数据结构会对应多种数据结构,如下表所示。

数据类型编码方式
OBJ_STRINGint、embstr、raw
OBJ_LISTLinkedList和ZipList(3.2以前)、QuickList(3.2以后)
OBJ_SETintset、HT
OBJ_ZSETZipList、HT、SkipList
OBJ_HASHZipList、HT

下面,我们现在介绍以下 String 数据类型,及其底层的编码方式。

Redis 数据结构 -- String

String 类型的基本介绍和命令

String 类型,也就是字符串类型,是Redis中最简单的存储类型。它可以存储字符串、整数或浮点数。下面是一些 String 类型常用的命令

  1. SET key value:设置指定 key 的值为指定的字符串或数字。

  2. GET key:获取指定 key 的值。

     bash 

    复制代码

    本地虚拟机redis:0>set key01 value01 "OK" 本地虚拟机redis:0>get key01 "value01"
  3. INCR key:将指定 key 的值加 1,如果该 key 不存在,则先将其设置为 0,再进行加 1 操作。

  4. DECR key:将指定 key 的值减 1,如果该 key 不存在,则先将其设置为 0,再进行减 1 操作。

  5. INCRBY key increment:将指定 key 的值增加指定的增量。

  6. DECRBY key decrement:将指定 key 的值减少指定的减量。

  7. APPEND key value:将指定的值追加到指定 key 的值的末尾。

  8. STRLEN key:返回指定 key 的值的长度。

  9. GETRANGE key start end:返回指定 key 的值的子字符串,根据起始位置和结束位置指定。

  10. SETRANGE key offset value:将指定 key 的值从指定偏移位置开始,替换为指定的字符串。

  11. MSET key1 value1 [key2 value2 ...]:同时设置多个 key 的值。(”[ ]” 中括号内表示可选)

  12. MGET key1 [key2 ...]:获取多个 key 的值。

💡 这里仅给出 SET、GET 命令,其他的请自行测试。这些命令只是 Redis String 类型命令的一小部分,Redis 还提供了其他更多的命令来处理 String 类型的数据。你可以参考 Redis 官方文档以获取完整的命令列表和详细的命令说明。

String 类型的底层实现

在 Redis 中,String 类型的数据结构并不是采用 C 语言中自带的字符串类型,C 语言中的数据结构存在很多问题,比如:

  • 获取字符串长度的需要通过运算
  • 非二进制安全
  • 不可修改

因此,String 在 Redis 中有其他三种编码方式: int、embstr、raw 。其中, raw 和 embstr 类型,都是基于动态字符串(SDS)实现的,下面我们先来看看动态字符串的结构是怎样的。

动态字符串(SDS)

动态字符串的结构体如下

 

c

复制代码

struct __attribute__ ((__packed__)) hisdshdr8 { uint8_t len; /* used */ uint8_t alloc; /* excluding the header and null terminator */ unsigned char flags; /* 3 lsb of type, 5 unused bits */ char buf[]; };

这里解释一下结构体中各个成员变量的作用:

  • len:已经保存的字符串字节数,不包含结束标示
  • alloc:申请的总的字节数,不包含结束标示
  • flags:不同的 SDS 的头类型,用来控制 SDS 的头大小
  • buf[]:真正存储数据

我们先来聊一下 flags 这个成员变量。在 redis 中其实定义了 5 个 SDS结构体(其中 hisdshdr5 已经弃用)如图所示。他们之间的主要区别在于 len 和 alloc 的长度不同。

在 redis 中,为了尽可能地节省内存空间,当字符串长度在不同的区间时,会选择不同的结构体,例如:

  • 当字符串长度在 0~255 个字节之间时,会选择 hisdshdr8 ,这样一来,用于表示字符串字节数和申请的总字节数的空间就会被大大节省,以此类推。

例如,一个包含字符串“name”的 sds 结构如下:

SDS之所以叫做动态字符串,是因为它具备动态扩容的能力,例如一个内容为 “hello” 的 SDS,假如我们要给这个 SDS 追加一段字符串 ”world” ,这里首先会申请新内存空间:

  • 如果新字符串小于1M,则新空间为扩展后字符串长度的两倍+1
  • 如果新字符串大于1M,则新空间为扩展后字符串长度+1M+1。

这种机制称为内存预分配。内存预分配可以减少进行内存重新分配的开销,减少内存碎片,使得 redis 的性能得到提高,空间利用率也得到提高。

String 的三种编码方式

RAW

  • raw 是 string 的基本编码方式,基于简单动态字符串(SDS)实现,存储上限为512mb。当一个字符串采用 raw 的编码方式的时候,它的结构如图所示。

EMBSTR

  • 如果存储在 SDS 中的数据小于等于 44 字节,则会采用 EMBSTR 编码,此时 **RedisObject 与 SDS 是一段连续空间。而不是像 RAW 的编码方式一样,由 ptr 指向另外一片空间,**申请内存时只需要调用一次内存分配函数,效率更高。结构如下,

💡 为什么是 44 字节?Redis 默认的内存分配器 jemalloc 分配内存大小的单位是 $2^n$ ,因此,如果分配的空间大小为 2、4 、8 … 字节等 $2^n$ 字节,就不会产生内存碎片。

redisObjecthisdshdr8len alloc flags三个成员变量加起来刚刚好是 16 + 4 = 20 字节,如果 char[] (数据大小)的大小为 44 字节时,加起来刚刚好是 64 字节,也即 262^626 不会产生内存碎片。

  • RAW 和 EMBSTR 的编码演示
 

c

复制代码

/* 44个v,采用embstr编码*/ 本地虚拟机redis:0>set key vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv "OK" 本地虚拟机redis:0>object encoding key "embstr" /* 45个v,采用embstr编码*/ 本地虚拟机redis:0>set key vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv "OK" 本地虚拟机redis:0>object encoding key "raw"

INT

  • 如果存储的字符串是整数值,并且大小在 LONG MAX 范围内,则会采用 INT 编码
  • 直接将数据保存在 RedisObject 的 ptr 指针位置(刚好8字节),不再需要SDS了。

  • INT 编码演示
 

c

复制代码

本地虚拟机redis:0>set key 12 "OK" 本地虚拟机redis:0>object encoding key "int"

写在最后:在使用 string 类型时,尽可能让其长度小于 44 字节,或者使用整数表示,使其使用 EMBSTR 和 INT 编码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/571879.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot+vue之java学习平台(java项目源码+文档)

风定落花生,歌声逐流水,大家好我是风歌,混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的java学习平台。项目源码以及部署相关请联系风歌,文末附上联系信息 。 💕💕作者:风歌&a…

档案库房太乱了怎么办?这个方法秒变高级!

全国有数以万计的大大小小的档案馆,其中有许多非常重要的机要档案,其历史和社会价值非常高,而档案保存的质量、档案的物理寿命、档案的防虫防霉都与库房的空气质量、温湿度息息相关。 解决档案高效管理及利用的安全问题越来越迫切&#xff0c…

在Ubuntu22.04上安装QQ~Linux

在Ubuntu22.04上安装QQ~Linux 0. 前言1. 下载deb安装包2. 使用dpkg安装deb包3. 安装完成,启动QQ3.1 点击图标打开3.2 使用命令行的方式打开 0. 前言 换Ubuntu当主力生产力了,并不是太喜欢vmware,所以我直接装到了硬盘里边,需要移…

SSM 如何使用 Kafka 实现消息队列?

SSM 如何使用 Kafka 实现消息队列? Kafka 是一个高性能、可扩展、分布式的消息队列系统,它支持多种数据格式和多种操作,可以用于实现数据传输、消息通信、日志处理等场景。在 SSM(Spring Spring MVC MyBatis)开发中…

iOS-最全的App上架教程

App上架教程 在上架App之前想要进行真机测试的同学,请查看《iOS- 最全的真机测试教程》,里面包含如何让多台电脑同时上架App和真机调试。 P12文件的使用详解 注意: 同样可以在Build Setting 的sign中设置证书,但是有点麻烦&…

软件开发项目成本控制的4大策略

1、构建责权利相结合的成本控制机制 需要对每个部门与个人的工作范围和工作职业有明确的界定,并赋予相应的权利以充分履行职责。在责任支配下高效完成工作进度时,需要给予一定的物质奖励。通过这样层层落实,逐级负责,从而做到责权…

VanillaNet:深度学习极简主义的力量

摘要 基础模型的核心是“更多不同”的理念,计算机视觉和自然语言处理方面的出色表现就是例证。然而,Transformer模型的优化和固有复杂性的挑战要求范式向简单性转变。在本文中,我们介绍了VanillaNET,这是一种设计优雅的神经网络架…

学会提问,ChatGPT可以帮你写出高质量论文

前言 ChatGPT 很火,火到大家以为他可以上天入地,上到天文,下到地理无所不能,但实际使用大家是不是会遇到如下的情况。 写论文步骤 今天,我们来探讨下怎样问ChatGPT,才能帮你写出一篇优秀的论文,…

【Java-Crawler】爬取动态页面(HtmlUnit、WebMagic)

爬取动态页面(WebMagic、HtmlUnit) 一、HtmlUnit的基本使用引入依赖一般使用步骤WebClient 的一些配置(上述一般步骤中的第二步) 二、案例(爬取CSDN首页)测试(WebMagicHtmlUnit)三、…

人机交互技术在车管所的应用探索

车管所作为交通管理的重要机构,承担着车辆登记、驾驶证办理、年检等重要职责,其工作效率和服务质量对于保障道路交通安全和畅通至关重要。而人机交互技术作为一种新兴的技术手段,可以为车管所提供更加高效、便捷的服务。因此,本文…

ESD防静电监控系统后台实时掌控现场静电防护情况

当静电积累到一定程度时,它可能会产生电击,从而对工人造成伤害。因此,工厂应该采取必要的预防措施,如提供防静电鞋和衣服,以保护工人免受静电伤害。 ESD防静电监控系统实现工业4.0技术要求,ESD物联技术稳定…

chatgpt赋能python:Python编程:接口程序的SEO优化方法

Python编程:接口程序的SEO优化方法 简介 接口程序是现代软件开发不可或缺的一部分,为应用程序提供外部数据访问和交互的方式。Python是一种功能强大的编程语言,在接口开发中也得到了广泛应用。本文将介绍如何使用Python编写有效的接口程序并…

新形式下安科瑞智能配网监控系统的应用研究

安科瑞 徐浩竣 江苏安科瑞电器制造有限公司 zx acrelxhj 摘要:随着经济和科技水平的快速发展,大型建筑变电所、配电房数量较多,分布区域广,配电运维部门人员对配电房的运维管理基本停留在传统的定期巡视、周期性检修、故障抢修…

对于质量保障,前端职能该做些什么?

目录 前言 1. 背景 2. 分析 2.1 前端自动化测试工具 2.1.1 针对工程代码的静态检查 2.1.2 针对部署产物的检查 2.1.3 性能测试 2.1.4 错误检测 2.1.5 容灾(白屏)检测 2.2 devOps 流程关联 2.2.1 提测卡点 2.2.2 发布卡点 3. 总结 3.1 严选…

RabbitMQ消息持久化机制

上一篇说到生产者消息确认机制,它可以确保消息投递到RabbitMQ的队列中,但是消息发送到RabbitMQ以后,如果MQ宕机,也可能导致消息丢失,所以提出了消息持久化。持久化的主要机制就是将信息写入磁盘,当RabbtiMQ…

机械师曙光16电脑开机自动蓝屏怎么解决?

机械师曙光16电脑开机自动蓝屏怎么解决?有的用户在使用机械师曙光16电脑的时候,遇到了一些系统问题,导致自己无法正常的开机使用电脑。因为电脑总会变成蓝屏,无法进行任何操作。那么这个情况怎么去进行问题的解决呢?来…

字节内部又推出最新spring进阶全家桶了!强烈建议人手一份!

前言 一份 Alibaba 内部强烈推荐的“玩转 Spring 全家桶的 PDF” ,小编也不是个吝啬的人,好的东西当然要一起分享咯。今天小编就带你一站通关 Spring全家桶,让你一路通关轻松斩获大厂 Offer! Spring 框架自 2002 年诞生以来一直…

如何真正开启docker远程访问2375

注意看官方文档 Configure remote access for Docker daemon | Docker Documentation 1. windows上Docker Desktop开启远程访问端口2375 系统版本: win10专业版 Docker Desktop版本:4.18.0 很简单勾上, 应用并重启即可 2. linux上开启 尝…

中兴通讯5G荣登《财富》2023年中国ESG影响力榜单

日前,《财富》正式对外公布“2023年中国ESG影响力榜单”,中兴通讯5G榜上有名,旨在表彰其在绿色发展、社会责任、公司治理方面做出的努力与贡献,值得一提的是,“中国ESG影响力榜单”是《财富》在去年创立的榜单&#xf…

怎么把ppt压缩到10m以内?

怎么把ppt压缩到10m以内?众所周知,压缩文件可以使得文件更加易于传输和存储。在PPT演示过程中,如果文件过大,可能会导致文件传输、下载或存储的速度变慢,影响用户使用体验。将PPT压缩到10M可以避免这种情况&#xff0c…