深入了解redis的string常用操作与存储机制

Redis中的字符串数据结构是动态的,类似于Java的ArrayList,允许修改且采用预分配冗余空间减少内存分配。字符串类型是Redis的基础,可以存储二进制安全的数据,包括图片。字符串操作如set、get、append等支持多种功能。字符串内部使用SDS结构,包含长度、容量和标志信息。当字符串小于44字节时,使用embstr编码,节省内存;超过则转为raw编码。Redis通过增量扩容策略管理字符串内存,小于1M时翻倍,超过1M时每次多分配1M。

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

【redis数据结构 – strings】

类似arrylist

字符串 string 是 Redis 最简单的数据结构。Redis 所有的数据结构都是以唯一的 key 字符串作为名称,然后通过这个唯一 key 值来获取相应的 value 数据。不同类型的数据结 构的差异就在于 value 的结构不一样。

有人说,如果只使用redis中的字符串类型,且不使用redis的持久化功能,那么,redis就和memcache非常非常的像了。这说明strings类型是一个很基础的数据类型,也是任何存储系统都必备的数据类型。

字符串类型的用法就是这么简单,因为是二进制安全的,所以你完全可以把一个图片文件的内容作为字符串来存储。

类似于arrylist的add

append key value //将给定的value添加在末尾

Redis 的字符串是动态字符串,是可以修改的字符串,内部结构实现上类似于 Java 的 ArrayList,采用预分配冗余空间的方式来减少内存的频繁分配,如图中所示,内部为当前字 符串实际分配的空间 capacity 一般要高于实际字符串长度 len。当字符串长度小于 1M 时, 扩容都是加倍现有的空间,如果超过 1M,扩容时一次只会多扩 1M 的空间。需要注意的是 字符串最大长度为 512M。

查询与设置

set 【key】 【value】//创建,可覆盖,setnx不能覆盖

get key//查询对应的

strlen key //获得值得长度

setnx key value //只有当key不存在时才能被设置成功

mset k1 v1 k2 v2 k3 v3//设置多个值

mget k1 k2//得到多个值

msetnx k1 v1 k2 v2//设置多个值,但是如果有一个k是存在的就都不成功

getrange key <起始位置><结束位置>//得到key的value的对应值

setrange key <起始位置><结束位置>//有覆盖的作用

setex key <过期时间><value>//设置的同时设置过期时间,单位秒

getset key value//设置新值的同时获得旧值

添加、删除、更新

append key value //将给定的value添加在末尾

incr key //将key存储得数字的value值加1

decr key//减一,为空也会执行
//在遇到数值操作时,redis会将字符串类型转换成数值

incrby/decrby key <长度> //将key存储得数字的value值加任意值

string的数据结构

除了记录实际数据,String类型还需要额外的内存空间记录数据长度、空间使用等信息,这些信息也叫作元数据。

string中SDS起到这个作用:

SDS:

struct SDS {
 int8 capacity; // 1byte
 int8 len; // 1byte
 int8 flags; // 1byte
    
 byte[] content; // 内联数组,长度为 capacity
}

另外,对于String类型来说,除了SDS的额外开销,还有一个来自于RedisObject结构体的开销。
RedisObject结构体会随着存储的string的大小而进行改变,接下来实验一下:

> set codehole abcdefghijklmnopqrstuvwxyz012345678912345678
OK
> debug object codehole
Value at:0x7fec2de00370 refcount:1 encoding:embstr serializedlength:45 lru:5958906 lru_seconds_idle:1
> set codehole abcdefghijklmnopqrstuvwxyz0123456789123456789
OK
> debug object codehole
Value at:0x7fec2dd0b750 refcount:1 encoding:raw serializedlength:46 lru:5958911 lru_seconds_idle:1

注意上面 debug object 输出中有个 encoding 字段,一个字符的差别,存储形式就发生 了变化。这是为什么呢? 为了解释这种现象,我们首先来了解一下 Redis 对象头结构体,因为Redis的数据类型有很多,而且,不同数据类型都有些相同的元数据要记录(比如最后一次访问的时间、被引用的次数等),所以,Redis会用一个RedisObject结构体来统一记录这些元数据,同时指向实际数据。

所有的 Redis 对象都有 下面的这个结构头:

在这里插入图片描述

struct RedisObject {
 int4 type; // 4bits
 int4 encoding; // 4bits
 int24 lru; // 24bits
  //4+4+24=32bits ==4bytes
    
 int32 refcount; // 4bytes
 void *ptr; // 8bytes,64-bit system
} robj
    //4+4+8=16bytes

一个RedisObject包含了8字节的元数据和一个8字节指针,这个指针再进一步指向具体数据类型的实际数据所在,例如指向String类型的SDS结构所在的内存地址。

每个对象都有个引用计数,当引用计数为零时,对象就会被销毁,内存被回收。ptr 指针将指向对 象内容 (body) 的具体存储位置。这样一个 RedisObject 对象头需要占据 16 字节的存储空间。

为了节省内存空间,Redis还对Long类型整数和SDS的内存布局做了专门的设计。

int 、embstr、raw

接着我们再看 SDS 结构体的大小,在字符串比较小时,SDS 对象头的大小是 capacity+3,至少是 3( 每个SDS的三个信息就占3个字节)。意味着分配一个字符串的最小空间占用为 19 字节 (16为redisobject的大小+每个SDS的三个信息大小)。

另外由于content是由/0 结尾的也会占一个字节

所以每个字符串至少占20字节

struct SDS {
 int8 capacity; // 1byte
 int8 len; // 1byte
 int8 flags; // 1byte
    
 byte[] content; // 内联数组,长度为 capacity
}

一方面,当保存的是Long类型整数时,RedisObject中的指针就直接赋值为整数数据了(int编码),这样就不用额外的指针再指向整数了,节省了指针的空间开销。

另一方面,当保存的是字符串数据,并且字符串小于等于44字节时,RedisObject中的元数据、指针和SDS是一块连续的内存区域,这样就可以避免内存碎片。这种布局方式也被称为embstr编码方式。

当然,当字符串大于44字节时,SDS的数据量就开始变多了,Redis就不再把SDS和RedisObject布局在一起了,而是会给SDS分配独立的空间,并用指针指向SDS结构。这种布局方式被称为raw编码模式。

在这里插入图片描述

如图所示,embstr 存储形式是这样一种存储形式,它将 RedisObject 对象头和 SDS 对 象连续存在一起,使用 malloc 方法一次分配。而 raw 存储形式不一样,它需要两次 malloc,两个对象头在内存地址上一般是不连续的。

而内存分配器 jemalloc/tcmalloc 等分配内存大小的单位都是 2、4、8、16、32、64 等 等,为了能容纳一个完整的 embstr 对象,jemalloc 最少会分配 32 字节的空间,如果字符 串再稍微长一点,那就是 64 字节的空间。如果总体超出了 64 字节,Redis 认为它是一个 大字符串,不再使用 emdstr 形式存储,而该用 raw 形式。

为什么是44呢?

由于每个字符串至少占20字节(RedisObject(16)+SDS内部信息(3)+/0的结尾(1)),64-20=44,留给字符内容的大小为44字节。因此超过44,embstr就变化为raw。

扩容策略

字符串在长度小于 1M 之前,扩容空间采用加倍策略,也就是保留 100% 的冗余空 间。当长度超过 1M 之后,为了避免加倍后的冗余空间过大而导致浪费,每次扩容只会多分 配 1M 大小的冗余空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值