Redis学习——1. 基础数据结构(一):简单动态字符串

前言

工作慢慢步入正轨,闲暇时间也多了起来,抽空充下电,岂不美哉?

想来想去,发现自己不足之处良多,何不选个使用较多的点作为切入,哈哈,于是有了这系列文章。作为记录,和大家分享。若有不足之处,还请言明,不胜感激。

本文主要是源自于对《Redis设计与实现》的学习,所以会有大量雷同,慎入。

 

介绍

众所周知,Redis有5种对象类型:string、list、hash、set、zset(排除Redis于2.8.9中新加入的HyperLogLog);这5种对象类型底层其实是由6种基础数据结构单个或多个组合实现,分别是:

  1. 简单动态字符串(SDS:Simple Dynamic String)
  2. 链表(Linked List)
  3. 字典(Dict)
  4. 跳跃表(Skip List)
  5. 整数集合(Int Set)
  6. 压缩列表(Zip List)

所以,接下来的章节会先对这几种基楚数据结构进行学习。

1、基础数据结构

1.1  简单动态字符串(SDS:Simple Dynamic String)

1.1.1  介绍

下图(1-1)为SDS在Redis中的实现:

struct sdshdr
{
	// 保存的字符串长度
	int len;
	// 未使用的长度
	int free;
	// 字节数组,用于保存字面量
	char buf[];
};

可以看出,redis使用一个结构体来存储字符串,该结构体包含了以下部分:

  1. len 字段存储了字符串的长度
  2. free 字段记录了buf数组中的可用空间大小
  3. buf 为char类型数组,保存了实际的数据

下表(1-2)为sds在内存中的结构:

sdshdr
len:5
free:4
buf:[ ] -------> R e d i s \0        

下表(1-3)表述了SDS实现和其特点之间的关系

sdshdr int len O(1)获取字符串长度
二进制安全
兼容部分C字符串函数
int free 杜绝缓冲区溢出
减少内存重新分配次数

 

1.1.2  特点

  • O(1)获取字符串长度

这个很容易理解,众所周知,c语言使用'\0'来作为字符结尾,所以要获取字符串的长度需要遍历整个字符串,时间复杂度为O(n),但Redis通过SDS中的len属性记录字符串长度,所以时间复杂度为O(1)

  • 杜绝缓冲区溢出

上文提到,C语言中对于字符串并不会记录其长度,其引发的问题除了上述以外,还有就是可能会造成缓冲区溢出。而Redis在每次对字符串进行修改之前,会先检测字符串的free属性值是否够用,若不够则会先进行空间扩展

  • 减少内存重新分配次数

在上文中有提到过空间扩展的概念,而Redis正是通过这样的方式来防止内存反复进行重新分配。

1. 空间预分配

在对字符串进行修改需要进行空间扩展时,Redis除了为SDS分配足够使用的空间外,还会额外分配一份未使用的空间。

a)当修改后的len属性小于1MB时,程序会分配同len一样的大小的未使用空间

b)当修改后的len属性大于1MB时,程序会分配同1MB大小的未使用空间

2. 空间惰性释放

当字符串的长度缩小时,Redis会将多余的空间记录在free属性中,等待将来使用。

  • 二进制安全

上文提到c语言使用'\0'来作为字符结尾,这回导致字符串中不能出现'\0'字符串,这使c字符串得无法保存二进制文件,而Redis的SDS通过len属性来区分字符串结尾,则无此问题。

  • 兼容部分C字符串函数

同样由于上述原因Redis的SDS可以使用部分的c语言字符串函数

 

 

你可能感兴趣的:(Redis学习笔记)