Redisbook学习笔记(1)sds

2014了休息了10多天没有写博客了不能把刚刚培养的习惯又荒废了那么就开始新年新旅途吧


Sds Simple Dynamic String简单动态字符串是Redis 底层所使用的字符串表示它被用
在几乎所有的Redis 模块中。
本章将对sds 的实现、性能和功能等方面进行介绍并说明Redis 使用sds 而不是传统C 字符
串的原因。

 

Sds 在Redis 中的主要作用有以下两个


1、 实现字符串对象StringObject

Redis 是一个键值对数据库key-value DB数据库的值可以是字符串、集合、列表等多种类
型的对象而数据库的键则总是字符串对象。

在Redis 中
一个字符串对象除了可以保存字符串值之外还可以保存long 类型的值所以为了严谨起见
这里需要强调一下当字符串对象保存的是字符串时它包含的才是sds 值否则的话它就
是一个long 类型的值。


举个例子以下命令创建了一个新的数据库键值对这个键值对的键和值都是字符串对象它
们都包含一个sds 值

wKioL1LRSOjBIprEAABoVcl19hE355.jpg

以下命令创建了另一个键值对它的键是字符串对象而值则是一个集合对象

wKiom1LRSVvAl03tAABzshWnTVw497.jpg

 

2.、在Redis 程序内部用作char* 类型的替代品

在Redis 中客户端传入服务器的协议内容、aof 缓存、返回给客户端的回复等等这些重要的内容都是由都是由sds 类型来保存的。

在C 语言中字符串可以用一个\0 结尾的char 数组来表示。
比如说hello world 在C 语言中就可以表示为"hello world\0" 。
这种简单的字符串表示在大多数情况下都能满足要求但是它并不能高效地支持长度计算和
追加append这两种操作
 每次计算字符串长度strlen(s)的复杂度为(N) 。

 对字符串进行N 次追加必定需要对字符串进行N 次内存重分配realloc。

 

考虑到这两个原因Redis 使用sds 类型替换了C 语言的默认字符串表示sds 既可以高效地
实现追加和长度计算并且它还是二进制安全的。

 

sds的实现由以下两个部分组成

typedef char *sds;
struct sdshdr{
//buf已占用长度
int len;
//buf剩余可用长度
int free;
//实际保存字符串数据的地方
char buf[];
};

 

作为例子以下是新创建的同样保存hello world 字符串的sdshdr 结构

struct sdshdr {
len = 11;
free = 0;
buf = "hello world\0"; // buf 的实际长度为len + 1
};

 

通过len 属性sdshdr 可以实现复杂度为(1) 的长度计算操作。
另一方面通过对buf 分配一些额外的空间并使用free 记录未使用空间的大小sdshdr 可
以让执行追加操作所需的内存重分配次数大大减少。

为了易于理解我们用一个Redis 执行实例作为例子解释一下当执行以下代码时Redis
内部发生了什么

wKioL1LRTYGwO06uAACjbrPMKfM661.jpg

首先SET 命令创建并保存hello world 到一个sdshdr 中这个sdshdr 的值如下

struct sdshdr {
len = 11;
free = 0;
buf = "hello world\0";
}

当执行APPEND 命令时相应的sdshdr 被更新字符串" again!" 会被追加到原来的
"hello world" 之后

struct sdshdr {
len = 18;
free = 18;
// 空白的地方为预分配空间共18 + 18 + 1 个字节
buf = "hello world again!\0 ";
}

 

注意当调用SET 命令创建sdshdr 时sdshdr 的free 属性为0 Redis 也没有为buf 创建
额外的空间——而在执行APPEND 之后Redis 为buf 创建了多于所需空间一倍的大小。


在这个例子中保存"hello world again!" 共需要18 + 1 个字节但程序却为我们分配了
18 + 18 + 1 = 37 个字节——这样一来如果将来再次对同一个sdshdr 进行追加操作只要
追加内容的长度不超过free 属性的值那么就不需要对buf 进行内存重分配。


比如说执行以下命令并不会引起buf 的内存重分配因为新追加的字符串长度小于18 

wKiom1LRTbzyk6uXAAB-61rvtIo552.jpg

再次执行APPEND 命令之后msg 的值所对应的sdshdr 结构可以表示如下

struct sdshdr {
len = 25;
free = 11;
// 空白的地方为预分配空间共18 + 18 + 1 个字节
buf = "hello world again! again!\0 ";
}

 

在目前版本的Redis 中SDS_MAX_PREALLOC 的值为1024 * 1024 也就是说当大小小于
1MB 的字符串执行追加操作时sdsMakeRoomFor 就为它们分配多于所需大小一倍的空间当
字符串的大小大于1MB 那么sdsMakeRoomFor 就为它们额外多分配1MB 的空间。

 

Note: 这种分配策略会浪费内存吗
执行过APPEND 命令的字符串会带有额外的预分配空间这些预分配空间不会被释放除非
该字符串所对应的键被删除或者等到关闭Redis 之后再次启动时重新载入的字符串对象将
不会有预分配空间。
因为执行APPEND 命令的字符串键数量通常并不多占用内存的体积通常也不大所以这一
般并不算什么问题。
另一方面如果执行APPEND 操作的键很多而字符串的体积又很大的话那可能就需要修
改Redis 服务器让它定时释放一些字符串键的预分配空间从而更有效地使用内存。

 

小结

 Redis 的字符串表示为sds 而不是C 字符串以\0 结尾的char*。
 对比C 字符串sds 有以下特性
– 可以高效地执行长度计算strlen
– 可以高效地执行追加操作append
– 二进制安全
 sds 会为追加操作进行优化加快追加操作的速度并降低内存分配的次数代价是多占
用了一些内存而且这些内存不会被主动释放。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值