1. Redis揭秘 - Redis字符串是如何实现的

本文深入探讨Redis中的字符串数据结构SDS,包括其存储方式、对象头结构、扩容策略及应用场景,如节拍序列记录和自增自减操作,强调了Redis在性能和内存管理上的优势。

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

若只如如初见-Redis字符串

String 数据结构是简单的 key-value 类型,value 不仅可以是 String,也可以是数字(当数字类型用 Long 可以表示的时候encoding 就是整型,其他都存储在 sdshdr 当做字符串)。
使用 Strings 类型,可以完全实现目前 Memcached 的功能,并且效率更高。
还可以享受 Redis 的定时持久化(可以选择 RDB 模式或者 AOF 模式),操作日志及 Replication 等功能。除了提供与 Memcached 一样的 get、set、incr、decr 等操作外,Redis 还提供了下面一些操作:

  1. LEN niushuai:O(1)获取字符串长度
  2. APPEND niushuai redis:往字符串 append 内容,而且采用智能分配内存(每次2倍)
  3. 设置和获取字符串的某一段内容
  4. 设置及获取字符串的某一位(bit)
  5. 批量设置一系列字符串的内容
  6. 原子计数器
  7. GETSET 命令的妙用,请于清空旧值的同时设置一个新值,配合原子计数器使用

string使用场景一般是存储简单的键值类型。比如用户信息,登录信息,配置信息等。
还有一种用得比较多的是string的incr/decr操作,即自减/自增操作。调用它是原子性的,无论调用多少次,都一一计算成功。例如需要增减库存的操作。
尽管string的value可以存储很大,甚至500多MB的容量。但是在性能上来说,我们尽量存储value的值不要过1MB。
在这里插入图片描述

千呼万唤始出来-Redis中的字符串数据结构

SDS结构

redis中的字符串是可以修改的字符串,又叫SDS。Simple Dynamic String。在内存中底层是一个带有长度信息的数组。
Redis规定字符串的长度不得超过512MB。创建字符串时,len和capacity一样长,不多分配冗余空间。因为绝大部分场景,都不使用append操作来动态修改字符串。

struct SDS<T> {
    T capacity; // 数组分配的空间 1byte
    T len;   // 字符串实际的长度 1byte
    byte flags; // 标志 1byte
    byte[] content; // 数组内容
}

不同的存储方式

长度 <= 44 byte,使用embstr形式存储。
长度 > 44 byte,使用raw方式存储。

Redis的对象头结构

struct RedisObject {
    int4 type;     // 4 bits
    int4 encoding;  // 4 bits
    int24 lru;      // 24 bits
    int32 refcount;  // 4 bytes
    void *ptr;       // 8 bytes,64位系统
}

embstr的存储方式,是将RedisObject对象头结构与SDS对象连续存储在一起,使用malloc方法一次分配。而raw则需要使用两次malloc分配内存,两个对象头在内存地址上一般是不连续的。
如果字符串的整体大小超过64字节,那么Redis认为这是一个大字符串,使用raw存储。

在这里插入图片描述

在这里插入图片描述

Redis的字符串扩容策略

字Redis的字符串长度在小于1MB之前,扩容采用加倍策略。
当字符串长度超过1MB之后,避免加倍后的冗余空间过大导致浪费,每次扩容申请1MB大小的冗余空间。

疑难杂症

Redis的embstr与raw存储形式为何划分界限是44个字节?

字符串整体超过64字节。redis就认为是一个大字符串。不再适合使用emdstr存储。使用raw存储。
由于Redis对象头 + SDS 结构头 + 字符串结尾符NULL 恰好是20bytes。64-20 = 40 bytes。

SDS结构体为何使用泛型?

当字符串比较短的时候,len和capacity可以使用byte与short表示。Redis为了对内存的极致优化,
不同长度的字符串,使用不同的结构体表示。

什么情况下,Redis的字符串需要使用append操作?

首先我们来看下Redis的Append命令。Redis Append 命令用于为指定的 key 追加值。
如果 key 已经存在并且是一个字符串, APPEND 命令将 value 追加到 key 原来的值的末尾。
如果 key 不存在, APPEND 就简单地将给定 key 设为 value ,就像执行 SET key value 一样。
场景模式:节拍序列(Time series)
APPEND 命令可以用来连接一系列固定长度的样例,与使用列表相比这样更加紧凑. 通常会用来记录节拍序列. 每收到一个新的节拍样例就可以这样记录:
APPEND timeseries “fixed-size sample”
在节拍序列里, 可以很容易地访问序列中的每个元素:
STRLEN 可以用来计算样例个数.
GETRANGE 允许随机访问序列中的各个元素. 如果序列中有明确的节拍信息, 在Redis 2.6中就可以使用GETRANGE配合Lua脚本来实现一个二分查找算法.
SETRANGE 可以用来覆写已有的节拍序列.
该模式的局限在于只能做追加操作. Redis目前缺少剪裁字符串的命令, 所以无法方便地把序列剪裁成指定的尺寸. 但是, 节拍序列在空间占用上效率极好.

使用定长字符串进行温度采样的例子(在实际使用时,采用二进制格式会更好).
redis> APPEND ts “0043”
(integer) 4
redis> APPEND ts “0035”
(integer) 8
redis> GETRANGE ts 0 3
“0043”
redis> GETRANGE ts 4 7
“0035”

参考链接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值