Redis常用数据类型(1)----String(字符串)

Redis常用数据类型之简单动态字符串

    在Redis的设计中其作者在设计之初并没有把C语言的传统字符串用来,而是设计了一个被其命名为简单动态字符串(SDS,simple dynamic string)的抽象类型来作为Redis的默认字符串表示。

SDS函数定义
strcut sdshrd{
	// 记录buf数组中已经使用的字节数量
	// 等价于SDS中所保存的字符串长度值
	int len;
	
	//记录buf数组中未使用的字节数量
	int free;

	// 字节数组,用来保存字符串
	char buf[];
}
既然与C语言不同,那么它们之间又有什么微妙的联系呢?且看下文
1. 获取字符串长度的复杂度不同

    在C中的字符串,并没有一个字段来单独存取字符串的长度,当我们需要获取一个字符串的长度的时候,需要遍历这个字符串来统计其包含的自符的个数,所以获取其长度的时间复杂度为O(n);
    在SDS简单动态字符串中,它会有一个单独的变量len来保存redis字符串中的个数。当我们写入字符串的时候,redis会统计字符个数并为其开辟空间,然后将其保存在len属性中。所以获取其长度的时间复杂度为O(1);

2. 防止缓冲区溢出

    因为C字符串本身不记录其长度,所以当我们并且一个字符串的时候,必须要保证有足够的空间来存储这些拼接的字符串,而一旦这个空间过小,不满足使用的空间,则会造成缓冲区溢出。所以c字符串在操作之前需要先分配足够空间,才能进行操作
    与C字符串不同,SDS的空间分配策略完全杜绝了发生缓冲区溢出的可能,当需要对SDS字符串进行修改的时候,首先会去检查SDS的空间是否满足需求的大小,如果不满足,则会将SDS空间的大小自动扩展到执行操作所需要的空间大小,然后才会执行操作,所以sds不需要手动修改空间大小,也不会出现缓冲区溢出的问题

3. 减少修改字符串时带来的内存分配的次数

    正如前面所讲,C字符串不记录本身的长度,所以对一个包含了N个字符的C字符串来说,其底层总是一个N+1个字符长的数组(最后的一个字符用来保存孔字符’\0’,因为所有的C字符串以’\0’作为结尾标识);正因为如此,每次增长或者缩短一个C字符串,程序总要对其底层的数组进行一次内存重新分配;
例如:
1. 增长一个字符串,进行append操作,在操作之前,首先需要通过内存重新分配来扩展底层数组空间的大小够用-- 如果忘记这一步操作就会造成缓冲区溢出。
2. 截取一个字符串,进行trim操作,在操作之钱,首先需要通过内存重新分配释放不再使用的那部分空间- 如果忘记这一步操作就会造成内存泄露。
    在Redis中,它作为一个数据库,经常被用在数据频繁修改而且还对速度要去特别严苛的场合,显然上面的缺陷会对其造成很大的影响。
    为了避免这种C字符串的缺陷,SDS通过未使用空间(free属性),解除了字符串长度和底层数组长度之间的关系。在SDS中,buf数组长度不一定就是字符数量加一,数组里面包含未使用的未使用的字节,这些字节的数量就有free属性记录。
    通过未使用空间,SDS实现了空间预分配和空间释放两种优化策略。
空间预分配策略:
    空间预分配用于优化字符串增长操作.当对一个SDS进行append操作,并且需要对字符空间进行扩展的时候,其不仅会为SDS修改分配所必要的空间还会为SDS分配额外的未使用空间。
1. 如果对SDS进行修改之后,SDS的长度(也即是len属性的值)将小于1MB,那么程序分配和len属性同样大小的未使用空间,这时SDSlen属性的值将和free属性的值相同。举个例子,如果进行修改之后,SDS的len将变成13字节,那么程序也会分配13字节的未使用空间, SDS的buf数组的实际长度将变成13+13+1=27字节(额外的一字节用于保存空字符’\0’)。
2. 如果对SDS进行修改之后,SDS的长度将大于等于1MB,那么程序会分配1MB的未使用空间。举个例子,如果进行修改之后,SDS的len将变成30MB,那么程序会分配1MB的未使用空间,SDS的buf数组的实际长度将为30MB + 1MB + 1byte。

    通过空间预分配策略,Redis可以减少连续执行字符串增长操作所需分配的内存重新分配的次数。
    在扩展SDS空间之前,SDS会先检查未使用空间是否足够,如果足够的话,就会直接使用未使用空间,而无须执行内存重分配。
    通过这种预分配策略, SDS将连续增长N次字符串所需的内存重分配次数从必需N次降低为最多N次。

惰性空间释放策略:
    惰性空间释放用于优化字符串缩短操作.当对一个SDS进行trim操作,程序并不会立即使用内存重新分配来回收缩短后多出来的字节,而是使用free属性来将这些空字节的数量进行记录,并等待将来使用。
    与此同时,SDS也提供了相应的API,让我们可以在有需要时,真正地释SDS的未使用空间,所以不用担心情性空间释放策略会造成内存浪费。
注意:
1. 如果字符串对象保存的是一个整型数字,则用int类型的编码保存这个对象
2. 如果字符串对象保存的是一个浮点数,则用字符串保存这个对象。即先将其转为字符串再对其进行存储
3. 如果字符串对象保存的是一个字符串值,并且这个字符串值的长度大于32字节,那么字符串对象将使用-一个简单动态字符串( SDS )来保存这个字符串值,并将对象的编码设置为raw。
4. 如果字符串对象保存的是一个字符串值,并且这个字符串值的长度小于等于32字节,那么字符串对象将使用embstr编码的方式来保存这个字符串值。

参考:

  1. Redis命令参考
  2. Redis官方文档
  3. 《redis设计与实现》
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值