您当前的位置:首页 > 电脑百科 > 数据库 > Redis

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

时间:2023-05-25 13:51:11  来源:今日头条  作者:一个即将退役的码农

redisObject 说起

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

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

  • unsigned type:4 :对象类型,分别是 string hash list set zset ,占 4 个 bit 位,如下所示
  • #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_STRING

int、embstr、raw

OBJ_LIST

LinkedList和ZipList(3.2以前)、QuickList(3.2以后)

OBJ_SET

intset、HT

OBJ_ZSET

ZipList、HT、SkipList

OBJ_HASH

ZipList、HT

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

Redis 数据结构 -- String

String 类型的基本介绍和命令

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

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

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

3.本地虚拟机redis:0>set key01 value01 "OK" 本地虚拟机redis:0>get key01 "value01"

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

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

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

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

8.AppEND key value:将指定的值追加到指定 key 的值的末尾。

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

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

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

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

13.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)

动态字符串的结构体如下

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

  • 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$ 字节,就不会产生内存碎片。

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

  • RAW 和 EMBSTR 的编码演示

INT

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

  • INT 编码演示

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



Tags: Redis   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
如何使用 Redis 实现消息队列
Redis不仅是一个强大的内存数据存储系统,它还可以用作一个高效的消息队列。消息队列是应用程序间或应用程序内部进行异步通信的一种方式,它允许数据生产者将消息放入队列中,然...【详细内容】
2024-03-22  Search: Redis  点击:(18)  评论:(0)  加入收藏
手动撸一个 Redis 分布式锁
大家好呀,我是楼仔。今天第一天开工,收拾心情,又要开始好好学习,好好工作了。对于使用 Java 的小伙伴,其实我们完全不用手动撸一个分布式锁,直接使用 Redisson 就行。但是因为这些...【详细内容】
2024-02-19  Search: Redis  点击:(40)  评论:(0)  加入收藏
关于 Redis ,这里有你不知道的知识
前言本篇文章不是一篇具体的教程,阿粉打算记录一下自己对Redis的一些思考。说来惭愧,阿粉刚接触Redis的时候只是简单地使用了一下,背了一些面试题,就在简历上写下了Redis这个技...【详细内容】
2023-11-24  Search: Redis  点击:(257)  评论:(0)  加入收藏
为什么单线程的 Redis 能那么快?
今天,我们来探讨一个很多人都很关心的问题:“为什么单线程的 Redis 能那么快?”首先,我要和你厘清一个事实,我们通常说,Redis 是单线程,主要是指 Redis 的网络 IO 和键值对读写是由...【详细内容】
2023-10-15  Search: Redis  点击:(323)  评论:(0)  加入收藏
2 个 .NET 操作的 Redis 客户端类库
Redis ,是一个高性能(NOSQL)的key-value数据库,Redis是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。Redi...【详细内容】
2023-10-11  Search: Redis  点击:(245)  评论:(0)  加入收藏
探索 Redis 与 MySQL 的双写问题
在日常的应用开发中,我们经常会遇到需要使用多种不同类型的数据库管理系统来满足各种业务需求。其中最典型的就是Redis和MySQL的组合使用。这两者拥有各自的优点,例如Redis为...【详细内容】
2023-10-04  Search: Redis  点击:(392)  评论:(0)  加入收藏
学透 Redis HyperLogLog,看这篇就够了
在移动互联网的业务场景中,数据量很大,系统需要保存这样的信息:一个 key 关联了一个数据集合,同时对这个数据集合做统计做一个报表给运营人员看。比如。 统计一个 APP 的日活、...【详细内容】
2023-09-25  Search: Redis  点击:(375)  评论:(0)  加入收藏
一文读懂 Redis 缓存系统
本文介绍了Redis缓存原理、详细解析了缓存模型、缓存一致性和缓存异常场景。尽管(关系型)数据库系统 (SQL) 带来了许多出色的属性,例如 ACID,但为了保持这些属性,数据库的性能在...【详细内容】
2023-09-21  Search: Redis  点击:(273)  评论:(0)  加入收藏
一台服务器上部署 Redis 伪集群
哈喽大家好,我是咸鱼。今天这篇文章介绍如何在一台服务器(以 CentOS 7.9 为例)上通过 redis-trib.rb 工具搭建 Redis cluster (三主三从)。redis-trib.rb 是一个基于 Ruby 编写的...【详细内容】
2023-09-05  Search: Redis  点击:(338)  评论:(0)  加入收藏
为什么创建 Redis 集群时会自动错开主从节点?
在《一台服务器上部署 Redis 伪集群》这篇文章中,咸鱼在创建 Redis 集群时并没有明确指定哪个 Redis 实例将担任 master,哪个将担任 slave,然而 Redis 却自动完成了主从节点的...【详细内容】
2023-09-05  Search: Redis  点击:(240)  评论:(0)  加入收藏
▌简易百科推荐
Redis 不再 “开源”,未来采用 SSPLv1 和 RSALv2 许可证
Redis 官方于21日宣布修改开源协议 —— 未来所有版本都将使用 “源代码可用” 的许可证 (source-available licenses)。具体来说,Redis 将不再遵循 BSD 3-Clause...【详细内容】
2024-03-27  dbaplus社群    Tags:Redis   点击:(12)  评论:(0)  加入收藏
Redis“叛逃”开源,得罪了几乎所有人
内存数据库供应商Redis近日在开源界砸下了一块“巨石”。Redis即将转向双许可模式,并实施更为严格的许可条款。官方对此次变更的公告直截了当:从Redis 7.4版本开始,Redis将在Re...【详细内容】
2024-03-25    51CTO  Tags:Redis   点击:(10)  评论:(0)  加入收藏
如何使用 Redis 实现消息队列
Redis不仅是一个强大的内存数据存储系统,它还可以用作一个高效的消息队列。消息队列是应用程序间或应用程序内部进行异步通信的一种方式,它允许数据生产者将消息放入队列中,然...【详细内容】
2024-03-22  后端Q  微信公众号  Tags:Redis   点击:(18)  评论:(0)  加入收藏
Redis不再 “开源”
Redis 官方今日宣布修改开源协议 —— 未来所有版本都将使用 “源代码可用” 的许可证 (source-available licenses)。具体来说,Redis 将不再遵循 BSD 3-Clause 开...【详细内容】
2024-03-21  OSC开源社区    Tags:Redis   点击:(9)  评论:(0)  加入收藏
在Redis中如何实现分布式锁的防死锁机制?
在Redis中实现分布式锁是一个常见的需求,可以通过使用Redlock算法来防止死锁。Redlock算法是一种基于多个独立Redis实例的分布式锁实现方案,它通过协调多个Redis实例之间的锁...【详细内容】
2024-02-20  编程技术汇    Tags:Redis   点击:(49)  评论:(0)  加入收藏
手动撸一个 Redis 分布式锁
大家好呀,我是楼仔。今天第一天开工,收拾心情,又要开始好好学习,好好工作了。对于使用 Java 的小伙伴,其实我们完全不用手动撸一个分布式锁,直接使用 Redisson 就行。但是因为这些...【详细内容】
2024-02-19  楼仔  微信公众号  Tags:Redis   点击:(40)  评论:(0)  加入收藏
工作中Redis有哪些好用的运维工具
工作中使用 Redis 时,如果大家公司没有专业运维,可能开发人员就会面临这些运维的工作,包括 Redis 的运行状态监控,数据迁移,主从集群、切片集群的部署和运维等等。本文我就从这三...【详细内容】
2024-02-06  waynaqua    Tags:Redis   点击:(56)  评论:(0)  加入收藏
批量执行Redis命令的四种方式!
前言在我们的印象中Redis命令好像都是一个个单条进行执行的,如果有人问你如何批量执行Redis命令,你能回答的上吗,或者说能答出几种方式呢?最容易想到的是Redis的一些批量命令,例...【详细内容】
2024-01-17  小许code  微信公众号  Tags:Redis命令   点击:(60)  评论:(0)  加入收藏
Redis 实现多规则限流的思考与实践
市面上很多介绍redis如何实现限流的,但是大部分都有一个缺点,就是只能实现单一的限流,比如1分钟访问1次或者60分钟访问10次这种,但是如果想一个接口两种规则都需要满足呢,我们的...【详细内容】
2024-01-03  架构精进之路  微信公众号  Tags:Redis   点击:(109)  评论:(0)  加入收藏
一站式Redis解决方案
Redis是一个高效的内存数据库,它支持包括String、List、Set、SortedSet和Hash等数据类型的存储,在Redis中通常根据数据的key查询其value值,Redis没有模糊条件查询,在面对一些需...【详细内容】
2024-01-01  大雷家吃饭    Tags:Redis   点击:(66)  评论:(0)  加入收藏
站内最新
站内热门
站内头条