Redis原理二之数据类型String

Redis中的每个对象由redisObject表示,包括type、encoding和ptr属性。对于字符串,有int、embstr和raw三种编码方式。embstr用于短字符串,减少内存分配;raw用于长字符串。sds作为字符串表示,提供动态扩展、减少内存重分配、二进制安全和快速遍历等优点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Redis原理二之数据类型String

Redis对象类型

Redis中的每个对象都由一个redisObject结构表示,该结构中和保存数据有关的三个属性分别是type属性、 encoding属性和ptr属性:
Redis使用对象来表示数据库中的键和值,每次当我们在Redis的数据库中新创建一个键值对时,我们至少会创建两个对象,一个对象用作键值对的健(键对象),另一个对象用作键值对的值(值对象)。redisObject占用了16个字节,先来看一下redisObject的存储结构:

/*
 1. Redis 对象 
*/
typedef struct redisObject {
    /*对象的类型.  4bits*/
    unsigned type;
    /*具体的数据结构,embstr、sds、  4bits*/
    unsigned encoding;
    /* 24位,对象最后一次被命令程序访问的时间,与内存回收有关*/
    /* LRU time (relative to global lru_clock) or
     * LFU data (least significant 8 bits frequency
     * and most significant 16 bits access time). */
    unsigned lru:LRU_BITS;
    /*引用计数。当refcount为0的时候,表示该对象已经不被任何对象引用,则可以进行垃圾回收了。4bytes */
    int refcount;
    /*指向对象实际的数据结构。8bytes*/
    void *ptr;
} robj;

在这里插入图片描述

预分配

  1. 阶段一: 插入新字符串
127.0.0.1:6379> set name tom
OK
127.0.0.1:6379> strlen name
(integer) 3
127.0.0.1:6379>

这里忽略len 和 free 消耗的8字节。
在这里插入图片描述
2. 阶段二: 追加字符串

127.0.0.1:6379> append name jack
(integer) 7
127.0.0.1:6379> strlen name
(integer) 7
127.0.0.1:6379> 

在这里插入图片描述
2. 阶段三: 追加相同字符串

127.0.0.1:6379> append name jack
(integer) 11
127.0.0.1:6379> strlen name
(integer) 11
127.0.0.1:6379>

在这里插入图片描述

string的存储方式

我们都知道,Redis是由C语言编写的。在C语言中,字符串标准形式是以空字符\0作为结束符的,但是Redis里面的字符串却没有直接沿用C语言的字符串。主要是因为C语言中获取字符串长度可以调用strlen这个标准函数,这个函数的时间复杂度是O(N),由于Redis是单线程的,承受不了这个时间复杂度。

对于不同的对象,Redis会使用不同的类型来存储。对于同一种类型type会有不同的存储形式encoding。对于string类型的字符串,其底层编码方式共有三种,分别为int、embstr和raw。
在这里插入图片描述

  • int
    当存储的字符串全是数字时,此时使用int方式来存储。如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型来表示,那么字符串对象会将整数值保存在字符串对象结构的ptr属性里面(将void*转换成1ong),并将字符串对象的编码设置为int。
	127.0.0.1:6379> set myint 12345
	OK
	127.0.0.1:6379> type myint
	string
	127.0.0.1:6379> object encoding myint
	"int"
	127.0.0.1:6379> 

在这里插入图片描述

  • embstr
    如果字符串对象保存的是一个字符串值,并且这个字符申值的长度小于等于44字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值,并将对象的编码设置为embstr。
	127.0.0.1:6379> set myembstr "abcdefg"
	OK
	127.0.0.1:6379> type myembstr
	string
	127.0.0.1:6379> object encoding myembstr
	"embstr"

在这里插入图片描述
为什么小于44字节时是embstr类型?
内存和cpu cache的访问统一都是64byte对齐的。也就是说即便你只需要读取1bit的数据,CPU还是会把64byte的数据从内存逐步扔到L1。
len(buf[]) = 64 - 16 - 1(由于字符串结尾需要一个\0占用一个字节) = 47。
而sdshdr最小需要3个字节,所以 Max(embstr) = 47 - 3 = 44 。
在这里插入图片描述

  • raw
    如果字符串对象保存的是一个字符串值,并且这个字符串值的长度大于等于44字节,那么字符串对象将使用一个简单动态字符串(SDS)来保存这个字符串值,并将对象的编码设置为raw。对于embstr和raw这两种encoding类型,其存储方式还不太一样。对于embstr类型,它将RedisObject对象头和SDS对象在内存中地址是连在一起的,但对于raw类型,二者在内存地址不是连续的。
	127.0.0.1:6379> set myraw 	"abcdefghiljlmnopqrstuvwxyzabcdefghijklmnopqrs"
	OK
	127.0.0.1:6379> object encoding myraw
	"raw"
	127.0.0.1:6379> type myraw
	string

在这里插入图片描述

sds 优点

  • 可动态扩展内存
    可以减 少追加 (append) 操作所需的内存重分配次数
  • 二进制安全
    sds在Redis中是实现字符串对象的工具,并且完全取代char*…sds是二进制安全的,它可以存储任意二进制数据,不像C语言字符串那样以‘\0’来标识字符串结束,
    因为传统C字符串符合ASCII编码,这种编码的操作的特点就是:遇零则止 。即,当读一个字符串时,只要遇到’\0’结尾,就认为到达末尾,就忽略’\0’结尾以后的所有字符。因此,如果传统字符串保存图片,视频等二进制文件,操作文件时就被截断了。
  • 快速遍历字符串
    SDS表头的len成员就保存着字符串长度,所以获得字符串长度的操作复杂度为O(1)。
  • 与传统的C语言字符串类型兼容
    SDS表头的buf被定义为字节数组,因为判断是否到达字符串结尾的依据则是表头的len成员,这意味着它可以存放任何二进制的数据和文本数据,包括’\0’
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值