redis专题笔记 - 简单动态字符串SDS


在开始这一部分之前,首先要明确几个概念上的区别,否则容易搞混淆:

  1. 面向用户的数据结构
  2. 底层数据结构
  3. 底层存储方式

面向用户数据类型

面向用户的数据类型,即我们常见的字符串、列表、哈希map等等。。。具体包括一下几类:

  1. Binary-safe strings: 二进制安全字符串
  2. Lists: 数组;字符串的集合,按照元素插入先后顺序排序
  3. Sets: 集合;字符串的集合,元素是唯一的、无排序的字符串
  4. Sorted sets: 有序集合;在集合的基础上,每个字符串元素绑定一个浮点数score,元素根据score排序
  5. Hashes: 字典(maps);元素由键值对组成,键值对都是字符串
  6. Bit arrays: 比特数组;本质是字符串,可以通过特定的命令,实现位操作
  7. HyperLogLogs:
  8. Streams:

底层数据结构

底层数据结构和我们常说的“redis五种常用数据结构”不太一样;比如:

  1. 二进制安全字符串的底层数据结构是简单动态字符串SDS
  2. 数组的底层数据结构是压缩列表普通双向链表(比较新的版本已经改为快速列表
  3. 哈希表的底层数据结构是支持渐进式rehash的dict结构
  4. 集合的底层同样是dict结构,只不过value是NULL
  5. 有序集合的底层数据结构是dict + 跳表

等等,详细的实现会在后面说到各种底层数据结构时提到

底层存储方式

底层存储方式又是另一个维度的概念,比如:

  1. 简单动态字符串SDS: 有embstrraw两种底层存储方式

目前感觉概念是比较复杂的,而且维度比较混乱,以上内容可能有误,也许后面理解更深刻后会回来修改

这一节的学习就从简单动态字符串SDS开始

简单动态字符串

数据结构

简单动态字符串的底层数据结构

struct SDS<T> {
	T capacity; // 此处用T,不用int;是因为redis对不同长度的字符串使用不同的数据类型;如byte或者short
	T len;
	byte flags;
	byte[] content;
}
  1. capaticy: 表示字符串容量
  2. len: 表示字符串实际长度
  3. flags: 固定一个字节,表示SDS header类型;SDS可以有五种不同header类型,以存储不同长度的字符串(redis对内存做了极致的使用)
    • #define SDS_TYPE_5 0
    • #define SDS_TYPE_8 1
    • #define SDS_TYPE_16 2
    • #define SDS_TYPE_32 3
    • #define SDS_TYPE_64 4
  4. content: 表示字符串存储的字符数组

SDS的一般特性

说起SDS的特性,一般会与C语言的字符串做对比。(因为其他高级语言可能也在某些方面做了优化,不太方面直接比较)

  1. O(1)时间复杂度获取字符串长度
    • SDS的数据结构中len字段记录SDS的长度,故获取SDS长度的时间复杂度为O(1)
    • C语言中获取字符串长度,需要遍历整个字符串,时间复杂度O(n)
  2. 避免字符串缓冲区溢出
    • SDS使用预分配策略,在长度要超过预分配容量时自动扩容
  3. 减少内存分配次数
    • SDS是可变长度的,由于capacity的存在,可以为字符串预分配内存空间供SDS增加长度,不用每次增加长度都分配内存
  4. 二进制安全
    • SDS使用长度len来定位字符串结束字符,在遍历字符串过程中,不会对数据进行假设和过滤,数据读取和数据写入完全一致
    • C语言中默认以\0结尾,如果二进制数据中本来就有\0字符,那么字符串将被截断

SDS存储方式

SDS的存储方式有两种,embstrraw:

  1. 当字符串长度小于44字节时,使用embstr存储
  2. 当字符串长度超过44字节时,使用raw存储
127.0.0.1:6379> set testKeyShort abcdefghijklmnopqrstuvwxyz
OK
127.0.0.1:6379> debug object testKeyShort // Short长度为26,使用embstr存储
Value at:0x7fb39b231500 refcount:1 encoding:embstr serializedlength:27 lru:13230045 lru_seconds_idle:12
127.0.0.1:6379>
127.0.0.1:6379> set testKeyLong abcdefghijklmnopqrstuvwxyz0123456789012345678
OK
127.0.0.1:6379> debug object testKeyLong // Long长度为45,使用raw存储
Value at:0x7fb39b017d30 refcount:1 encoding:raw serializedlength:46 lru:13230051 lru_seconds_idle:9

Redis头结构

所有的redis对象都有下面这样的头结构:

type RedisObject {
	int4 type;           // 4bits, 对象类型;redis目前可以表示16种数据类型,如SDS、ziplist等
	int4 encoding;    // 4bits, 存储形式;同一个类型的不同的存储形式;如SDS可以有embstr、raw两种存储形式
	int24 lru;            // 3bytes, 每条数据的LRU信息
	int32 refcount;   // 4bytes, 4个字节记录对象的引用计数,用于内存回收
	void *ptr;          // 8bytes(64位操作系统),指向对象内容的指针
}

所以,每个对象的头结构一般需要0.5 + 0.5 + 3 + 4 + 8 = 16个bytes的存储空间

SDS结构体大小

在字符串比较小时,SDS数据结构中 T = int8

type SDS {
	int8 capacity;
	int8 len;
	int8 flags;
	bytes[] content;
}

所以,一个SDS结构体的大小为capaticy + 3,其中capacity为字符串的容量,3则表示capaticy/len/flags各占一个字节空间

embstr VS raw

现在我们知道,一个SDS对象 = RedisObjct + SDS,即对象头 + SDS数据结构本身

  1. embstr的存储形式是将**RedisObjectSDS**连续存储,其内存地址连续,使用malloc方法一次分配内存
  2. raw的存储形式是将RedisobjectSDS分开存储,其内存地址不连续,需要使用两次malloc分配内存

一般内存分配器jemalloctcmalloc分配内存的大小单位都是2/4/8/16/32/64,当一个SDS分配内存小于64个字节时,使用embstr存储,否则使用raw存储

对于一个完整的SDS,其已有固定长度为16 + 3 = 19,即一个16字节的对象头和3个字节的SDS固定字段,Redis字符串还需要一个字节已NULL结尾,所以,当一个内存分配单位为64bytes时,字符串的最大容量

capacity = 64 - 16 - 3 - 1 = 44

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值