Redis 总共支持五种数据类型: string,hash,list,set 及 zset. 这里介绍字符串类型的实现
首先了解字符串对象的结构
- // redis 对象内存分配, 列出主要相关的属性
- redisObject {
- // 对于字符串对象, type = REDIS_STRING
- type
- // 字符串的底层编码, 有三种: int,raw,embstr, 后文介绍
- encoding
- // 指向实际保存字符串内容的空间
- ptr
- // 还有其他属性
- ......
- }
上面的 redisObject 不实际保存字符串内容, 而是通过 ptr 指向实际保存字符串内容的空间, 叫 sdshdr(Simple Dynamic String hdr 简单动态字符串)
- struct sdshdr {
- // 记录 buf 数组中已使用的字节数, 等同于字符串长度(不包括结尾的 \ 0)
- int len;
- // 记录 buf 数组中未使用的字节数
- int free;
- // 实际保存字符串的字节数组
- char buf[];
- }
这个 sdshdr 比较厉害, 在字符串变长的时候可以预申请额外内存, 缩短时不直接释放内存以备未来变长使用, 等.
sdshdr 的详细看这里: https://www.cnblogs.com/loveCheery/p/9133343.html
字符串编码
字符串对象有三种可能的底层编码: int,raw,embstr.
这三种编码格式是字符串对象底层空间分配的不同, 对上层调用没有区别, 不同编码的字符串对象在执行命令时效果是一样的
int 编码
如果字符串保存的值是整数, 范围在 long 之内, 那么 encoding 会被设置为 int, 并且直接将证书值保存在 ptr 里(ptr 不再指向一个复杂的 sds 结构了, 直接是整数值)
- // 插入 long 之内的值为整数的字符串
- 127.0.0.1:6379> set test "123"
- OK
- 127.0.0.1:6379> object encoding test
- "int"
- // 插入 long 的最大值 2^63-1
- 127.0.0.1:6379> set test "9223372036854775807"
- OK
- 127.0.0.1:6379> object encoding test
- "int"
- // 插入 2^63, 就不是 int 编码了
- 127.0.0.1:6379> set test "9223372036854775808"
- OK
- 127.0.0.1:6379> object encoding test
- "embstr"
raw 编码
如果字符串保存的值长度比较大, 那么 encoding 为 raw, 值使用 sds(简单动态字符串),ptr 指向这个 sds 的空间
比较大:Redis 设计与实现里说长度大于 39 字节时, n 多文章说长度大于 32 字节时, 我测试返回长度大于 44 时才行, 没查到是配置可选的还是 redis 版本指定的
总的来说就是对于大字符串, redis 采用一套机制来控制字符串变长变短时的内存分配策略, 实现是用的 sds
embstr 编码
如果字符串保存的值不是整数, 并且长度不是很大, 那么 encoding 为 embstr, 值也是 sds,ptr 指向这个 sds 的空间
embstr 编码是专门用于保存段字符串的一种优化编码方式
embstr 和 raw 的实际字符串存储都是用了 sds, 区别在于:
embstr 编码的字符串对象, 其所有数据保存在一块连续的内存空间中(redisObject 和其 ptr 指向的 sds 在连续的内存空间), 只需要一次内存分配操作
raw 编码的字符串对象, redisObject 和其 ptr 指向的 sds 是不连续的, 需要两次内存分配
embstr 的内存空间连续的优势
其创建时的内存分配, 释放时的内存回收次数都只有 1 次
方便预读取一段空间内的数据做缓存
其他
embstr 只读, 没有修改方法. 当对 embstr 编码的字符串做修改时, 会先转为 raw, 再修改
- 127.0.0.1:6379> set test "hello"
- OK
- 127.0.0.1:6379> object encoding test
- "embstr"
- 127.0.0.1:6379> append test "world"
- (integer) 10
- 127.0.0.1:6379> object encoding test
- "raw"
int 编码在执行 append 前, 也会先转为 raw, 再修改
int 编码执行整数计算时 (比如 incrby) 直接使用整数编码
raw 和 embstr 编码, 执行整数方法 (比如 incrby) 时, 如果其内容为 long 内的整数, 则先转为 int 再执行, 执行完还是 int. 如果内容不是 long 内的整数, 则向客户端抛出异常
字符串命令详细实现:
来源: https://www.cnblogs.com/loveCheery/p/9145191.html