Redis数据结构之String

本文详细介绍了Redis中String类型,包括其作为常见数据存储类型的特点、适用场景,如对象存储、缓存、计数器等。讨论了String的底层编码方式,如EMBSTR、INT和RAW编码,以及它们的适用条件。同时,文章深入探讨了String的底层数据结构SDS,包括Redis 6.0版本的改进。最后,列举并解析了若干String类型的常用命令,如SET、GET、APPEND等。

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

一、String类型简介

String是Redis中最常见的数据存储类型,它存储可以是数字、字符串或二进制数据。由于 Redis 的字符串是二进制安全的,因此可以用来存储图片、视频等二进制数据。其基本编码方式是有OBJ_ENCODING_INT、OBJ_ENCODING_EMBSTR、OBJ_ENCODING_RAW基于简单动态字符串SDS(simple dynamic string)实现,存储上限为512MB。如果存储的SDS长度小于44字节,则会采用EMBSTR编码,此时RedisObject与SDS是一段连续空间,申请内存时只需要调用一次内存分配函数,效率更高;当存储的是整型,且能用8个字节进行表示,则Redis会选择使用INT编码来存储,此时redisObject对象中的ptr指针直接替换为long类型。

 二、String使用场景
  • 对象存储,存储图片、视频等二进制数据;
  • 缓存数据,提高访问速度和降低数据库压力。
  • 计数器,利用 incr 和 decr 命令实现原子性的加减操作。
  • 分布式锁,利用 setnx 命令实现互斥访问。
  • 限流,利用 expire 命令实现时间窗口内的访问控制。
三、底层编码方式

        在 Redis中String 数据类型的底层实现采用了不同的编码方式,具体取决于存储的值的类型和大小。下面是 Redis中 String 数据类型的三种底层编码方式:

①EMBSTR编码

        当存储的值为字符串且长度小于等于 44字节时(旧版本39),Redis 使用EMBSTR编码。在 EMBSTR编码中,此时RedisObject与SDS是一段连续空间,申请内存时只需要调用一次内存分配函数。

②INT编码

        当存储的值为整数且值的大小可以用 LONG类型表示时,Redis 使用INT编码。String对象的实际值会被存储ObjectRedis对象ptr指针(8个字节)中,不再需要SDS了。

③RAW编码

        当存储的值为字符串长度大于44字节时,Redis使用RAW储存,此时RedisObject与SDS是两端内存,需要分配2次内存,性能较差,其中最大存储上限是512MB。

 四、String底层数据结构

         Redis的String类型的实际储存结构都是简单动态字符串SDS(Simple Dynamic Strings),OBJ_ENCODING_INT、OBJ_ENCODING_EMBSTR、OBJ_ENCODING_RAW三种编码方式只是负责储存元数据(字符长度、SDS指针等)。

SDS包含3种编码类型

  • OBJ_ENCODING_EMBSTR:占用64Bytes的空间,存储44Bytes的数据
  • OBJ_ENCODING_RAW:存储大于44Bytes的数据
  • OBJ_ENCODING_INT:存储整数类型

        Redis 6.0版本相比Redis 5.0版本在SDS底层数据结构上进行了一些改进和优化。Redis 6.0中的SDS仍然包含三个成员变量len、free和buf,但是buf不再是一个字符数组,而是一个unsigned char类型的数组。此外,在Redis 6.0中新增了四个SDS类:sdsHdr5、sdsHdr8、sdsHdr16和sdsHdr32。这四个类分别代表SDS字符串的头部数据结构,用于存储SDS字符串的长度和空闲空间,以及标记SDS字符串的类型。

        Redis 6.0版本中的这些改进和优化可以提高SDS的效率和灵活性。通过使用unsigned char类型的数组来存储SDS字符串,可以更好地处理二进制数据和字符编码。而新增的四个SDS类可以更灵活地处理不同长度的SDS字符串,减少内存浪费。此外,为了提高效率,Redis 6.0版本中还对SDS的内存管理进行了优化,避免了频繁的内存分配和释放操作。

①SDS类对象

typedef struct sdshdr {
//buf已保存的字符串字节数,不包含结束标示
uint32_t len;
//buf申请的总的字节数,不包含结束标示
uint32_t alloc;  
//不同SDS的头类型,用来控制SDS的头大小
unsigned char flags;
//用于存储字符串数据
char buf[];  
}sds;

  • SDS_TYPE_5:这个比较特殊,注释上说未被使用。
  • SDS_TYPE_8: 当len 小于 1 << 8,也就是小于256时,变量len和alloc用uint8类型。
  • SDS_TYPE_16: 当len 小于 1 << 16,也就是小于65536时,变量len和alloc用uint16类型。
  • SDS_TYPE_32: 当len 小于 1 << 32,也就是小于4294967296时,变量len和alloc用uint32类型。
  • SDS_TYPE_64: 当len 大于等于 1 << 32,也就是大于等于4294967296时,变量len和alloc用uint64类型。

EMBSTR底层数据结构

        当String的长度小于等于44字节时(旧版本39),Redis使用EMBSTR储存,此时RedisObject与SDS是一段连续空间,申请内存时只需要调用一次内存分配函数。

QA:为啥Redis底层使用44字节作为限制编码方式呢?

        对于RedisObject包装的统一对象结构大小是16字节,SDS对象头是3字节,字符串大小是44字节,对象尾是1字节,加起来是64字节,而Redis默认使用jemalloc而不是glibc的malloc内存分配算法,这种算法是2的幂次方去分配内存的,所以44字节正好符合Redis内存分配大小,避免了内存碎片的产生。

INT底层数据结构

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

RAW底层数据结构

        当String的长度大于44字节时,Redis使用RAW储存,此时RedisObject与SDS是两端内存,需要分配2次内存,性能较差,其中最大存储上限是512MB。

注:一旦转为RAW,就算进行删减操作使字节小于44字节,也不会再转回EMBSTR。

五、String常用命令

SET key value:设置指定 key 的值,如果key的值已存在就覆盖旧值。

语法:

localhost:0>set name ‘张三’

"OK"

可用版本: >= 1.0.0

返回值: 在 Redis 2.6.12 以前版本, SET 命令总是返回 OK 。

GET key:获取指定key的指,如果key不存在返回null。

语法:

localhost:0>get name1

null

localhost:0>get name

"张三"

可用版本: >= 1.0.0

返回值: 返回 key 的值,如果 key 不存在时,返回 nil。 如果 key 不是字符串类型,那么返回一个错误。

GETRANGE key start end:获取指定key中字符串值的子字符。

语法:

localhost:0>set string 'set redis string type value is null'

"OK"

localhost:0> GETRANGE string 0 2

"'set"

localhost:0> GETRANGE string 0 -1

"set redis string type value is null"

可用版本: >= 2.4.0

返回值: 截取得到的子字符串。

GETSET key value,设置指定 key 的值设为 value ,并返回 key 的旧值(old value)。

语法:

localhost:0>getset name '李四'

"张三"

localhost:0>getset name1 'lili'

null

可用版本: >= 1.0.0

返回值: 返回给定 key 的旧值。 当 key 没有旧值时,即 key 不存在时返回 null 。

GETBIT key offset对 key 所储存的字符串值,获取指定偏移量上的位(bit)

语法:

# 对不存在的 key 或者不存在的 offset 进行 GETBIT, 返回 0

localhost:0>get name2

null

localhost:0>getbit name2 1

"0"

localhost:0>getbit name2 1024

"0"

localhost:0>getbit name 2

"1"

localhost:0>getbit name 1024

"0"

可用版本: >= 2.2.0

返回值: 字符串值指定偏移量上的位(bit)。

MGET key1 [key2..] 获取所有(一个或多个)定 key 的值。

语法:

localhost:0>mget name name1

1) "李四"

2) "lili"

localhost:0>mget name name1 name2

1) "李四"

2) "lili"

3) null

可用版本: >= 1.0.0

返回值: 一个包含所有给定 key 的值的列表,如果某个 key 不存在,那么这个 key 返回 null

⑦ SETBIT key offset value对 key 所储存的字符串值,设置或清除指定偏移量上的位(bit)

语法:

localhost:0>getbit name1 1

"1"

localhost:0>setbit name1 1 0

"1"

localhost:0>getbit name1 1

"0"

可用版本: >= 2.2.0

返回值: 指定偏移量原来储存的位。

⑧ SETEX key seconds value将值 value 关联到指定 key ,并将 key 的过期时间设为 seconds (以秒为单位),如果 key 已经存在, SETEX 命令将会替换旧的值。

localhost:0>setex name2 10 '赵五'

"OK"

可用版本: >= 2.0.0

返回值: 设置成功时返回 OK 。

⑨ SETNX key value :只有指定的key 不存在时,才能设置 key 的值。

语法:

localhost:0>setnx name3 '孙六'

"1"

localhost:0>setnx name2 '孙六'

"0"

可用版本: >= 1.0.0

返回值: 设置成功,返回 1 。 设置失败,返回 0 。

⑩ SETRANGE key offset value :用 value 参数覆写指定定 key 所储存的字符串值,从偏移量 offset 开始。

语法:

localhost:0>get string

"set redis string type value is null"

localhost:0>setrange string 10 "List type value is [linkedlist]"

"41"

localhost:0>get string

"set redis List type value is [linkedlist]"

可用版本: >= 2.2.0

返回值: 被修改后的字符串长度。

⑪ STRLEN key返回指定 key 所储存的字符串值的长度

语法:

localhost:0>strlen string

"41"

localhost:0>strlen string1

"0"

可用版本: >= 2.2.0

返回值: 字符串值的长度。 当 key 不存在时,返回 0。

⑫ MSET key value [key value ...]:同时设置一个或多个 key-value 对,对于已经存在的key,覆盖旧值。

语法:

localhost:0>set key1 goodbye

"OK"

localhost:0>mset key1 hello key2 world

"OK"

localhost:0>get key1

"hello"

localhost:0>get key2

"world"

可用版本: >= 1.0.1

返回值: 总是返回 OK 。

⑬ MSETNX key value [key value ...]同时设置一个或多个 key-value 对,当且仅当所有给定 key 都不存在。

语法:

localhost:0>msetnx key2 reset key3 msetnx key4 value

"0"

localhost:0>msetnx key3 msetnx key4 value

"1"

可用版本: >= 1.0.1

返回值: 当所有 key 都成功设置,返回 1 。 如果所有指定 key 都设置失败(至少有一个 key 已经存在),那么返回 0 。

⑭ PSETEX key milliseconds value:该命令和 SETEX 命令相似,但它以毫秒为单位设置 key 的生存时间,而不是像 SETEX 命令那样,以秒为单位。

语法:

localhost:0>PSETEX key4 1000 hello

"OK"

localhost:0>get key4

null

可用版本: >= 2.6.0

返回值: 设置成功时返回 OK 。

⑮ INCR key将 key 中储存的数字值增一,数值限制在 64 位(bit)有符号数字表示之内。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCR 操作。

语法:

localhost:0>incr key3

"ERR value is not an integer or out of range"

localhost:0>get key4

null

localhost:0>incr key4

"1"

localhost:0>get key4

"1"

localhost:0>incr key4

"2"

可用版本: >= 1.0.0

返回值: 执行 INCR 命令之后 key 的值。

⑯ INCRBY key increment:指定的key 所储存的值加上给定的增量值(increment),数值限制在 64 位(bit)有符号数字表示之内。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 INCRBY 操作 。

语法:

localhost:0>incrby key3 2

"ERR value is not an integer or out of range"

localhost:0>get key4

"2"

localhost:0>incrby key4 3

"5"

localhost:0>incrby key5 3

"3"

localhost:0>get key4

"5"

localhost:0>get key5

"3"

可用版本: >= 1.0.0

返回值: 加上指定的增量值之后, key 的值。

⑰ INCRBYFLOAT key increment:将指定的key 所储存的值加上给定的浮点增量值(increment),如果 key 不存在,那么 INCRBYFLOAT 会先将 key 的值设为 0 ,如果key非数值类型,执行会报错。

语法:

localhost:0>get key5

"3"

localhost:0>incrbyfloat key5 0.1313

"3.1313"

localhost:0>get key6

null

localhost:0>incrbyfloat key6 3.1415

"3.1415"

localhost:0>set key6 hello

"OK"

localhost:0>incrbyfloat key6 3.1415

"ERR value is not a valid float"

可用版本: >= 2.6.0

返回值: 执行命令之后 key 的值。

⑱ DECR key将 key 中储存的数字值减一,该命令数值限制在 64 位(bit)有符号数字表示之内。如果值包含错误的类型,或字符串类型的值不能表示为数字,那么返回一个错误。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECR 操作。

语法:

localhost:0>get key7

null

localhost:0>decr key7

"-1"

localhost:0>set key7 hello

"OK"

localhost:0>decr key7

"ERR value is not an integer or out of range"

可用版本: >= 1.0.0

返回值: 执行命令之后 key 的值。

⑲ DECRBY key decrement:指定key 所储存的值减去给定的减量值(decrement),所操作的数值限制在 64 位(bit)有符号数字表示之内。如果值包含错误的类型或字符串类型的值不能表示为数字,那么返回一个错误。如果 key 不存在,那么 key 的值会先被初始化为 0 ,然后再执行 DECRBY 操作。

语法:

localhost:0>get key4

"5"

localhost:0>decrby key4 2

"3"

localhost:0>get key5

"3.1313"

localhost:0>decrby key5 2

"ERR value is not an integer or out of range"

localhost:0>get key6

"hello"

localhost:0>decrby key6 2

"ERR value is not an integer or out of range"

可用版本: >= 1.0.0

返回值: 减去指定减量值之后, key 的值。

⑳ APPEND key value:指定的 key 已经存在并且是一个字符串, APPEND 命令将指定的 value 追加到该 key 原来值(value)的末尾。

语法:

localhost:0>get key5

"3.1313"

localhost:0>append key5 ':圆周率'

"18"

localhost:0>get key5

"3.1313:圆周率"

localhost:0>get key6

"hello"

localhost:0>append key6 world

"10"

localhost:0>get key6

"helloworld"

localhost:0>get key7

null

localhost:0>append key7 hello

"5"

localhost:0>get key7

"hello"

可用版本: >= 2.0.0

返回值: 追加指定值之后, key 中字符串的长度。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值