Redis学习(一)-- Redis及SDS初识

1.Redis简介

Redis(REmote DIctionary Server)是使用C语言编写的、开源的、支持网络的、基于内存的、可选持久化的键值对数据库。

2009年第一版,2018年redis 5.0.0,2019年redis 6,目前最新。

1.1 Redis优点

  1. 内存型数据库,读写速度快
  2. 单线程(一个线程处理网络请求,IO多路复用),避免了线程之间的切换,不存在加锁、解锁等同步操作
  3. 支持复杂的数据类型,key value的value可以是多种类型:string list set zset hash
  4. 支持数据持久化:RDB AOF RDB&AOF 三种方案,重启后数据可回复
  5. 支持主从结构,集群模式

1.2 与Memcached对比

  1. 内存型数据库
  2. 多线程
  3. 只支持string、二进制类型
  4. 不支持数据持久化,重启后数据丢失
  5. 不支持集群模式,需要依赖客户端实现在集群中分片写入数据

2.简单动态字符串-SDS

2.1 SDS简介

简单动态字符串(Simple Dynamic Strings,SDS)是Redis的基本数据结构之一,也是Redis高效工作的武器之一。SDS用于存储字符串和整形数据,并且保证了二进制安全(后续会说明)。

2.2 SDS数据结构

struct sds {
    int len;   //buf中已占用字节数,即当前长度
    int free;  //buf中剩余可用字节数
    char buf[];//柔性数组,可伸缩(可扩容)
}

2.3 SDS相对于char*的优点

(1)快速获取长度和剩余空间

根据统计变量len,free可以更快的得到字符串的长度和可用字节数,O(1)

(2)二进制安全

C语言中,"\0"表示字符串的结束,如果字符串中本身就有"\0",字符串会被截断,该问题称为非二进制安全。SDS中可以根据len可知字符串是否终止,不必依赖于"\0"。

(3)防止越界

当对SDS进行修改、合并、追加等操作时,可以根据len free等进行边界检查(还有自动扩容及类型转换),防止操作越界。

(4)空间预分配和惰性释放

(4.1)空间预分配

用于SDS字符串的增长:当对SDS进行拼接修改时,会进行边界检查,如有需要,会自动进行空间扩展,为其分期所必须的空间以及额外的未使用空间(sdsMakeRoomFor)。

(4.2)惰性空间释放

用于SDS字符串的缩短(释放):并非使用内存重分配释放多出来的内存,而是通过重置len free记录,

新的数据可以覆盖写,不用重新申请内存。

当然,SDS也提供了相应的API,使其在需要时真正地释放未使用空间,避免惰性空间释放导致的空间浪费。

(5)兼容C语言处理字符串的函数

SDS对上层暴露的指针不是SDS结构体的指针,而是指向柔性数组buf的指针。可直接对其使用C语言的字符串处理函数。

2.4 SDS优化

2.4.1 SDS多容量类型优化(头部优化)

上述SDS结构,对于短字符串来说,内容可能只有1字节,但头部却占了4个字节,浪费空间

对于很长的字符串来说,len和free都是4个字节,能存储的字符数量最多也只能为232

(1)5种类型

分为5种类型(头部长度小于1字节、1字节、2字节、4字节、8字节),使用1字节的标志字段flags来标识类型(sdshdr5种flags也用于记录buf长度)。sdshdr5 sdshdr8 sdshdr16 sdshdr32 sdshdr64

sdshdr5
struct __atribute__((__packed)) sdshdr5{
    unsigned char flags; //低3位表示类型,高5位表示长度
    char buf[];
}

sdshdr5种flags的低3位表示类型(23=8),高5位表示长度(25=32 ,即可用于表示长度小于32的短字符串)

sdshdr16

sdshdr8(最长28) sdshdr16(最长216) sdshdr32(最长232=4GB) sdshdr64(最长264=224T) 结构相同。

struct __atribute__((__packed)) sdshdr8{
    unit8_t len;        //已使用长度
    unit8_t alloc;      //总长度,1字节
    unsigned char flags; //低3位表示类型,高5位预留
    char buf[];
}
struct __atribute__((__packed)) sdshdr16{
    unit16_t len;        //已使用长度
    unit16_t alloc;      //总长度,2字节
    unsigned char flags; //低3位表示类型,高5位预留
    char buf[];
}
...

这里以sdshdr16为例:
在这里插入图片描述

(2)结构体对齐

__atribute__((__packed__))修饰,使结构体由按照变量大小的最小公倍数(sdshdr16是2,sdshdr32是24)字节对齐变为按1字节对齐,一下以sdshdr32为例:

在这里插入图片描述

改为1字节对齐后:

  • ​ 节省了3字节
  • ​ 能使用buf[-1]找到flags。否则还要对不能的结构进行处理buf[-4] buf[-8]…等,更复杂。

2.4.2 SDS扩容

当SDS字符串进行拼接操作时,可能会出现buf柔性数组的长度不能够容纳拼接后的字符串,因此再SDS拼接操作中,首先进行扩容检查(sdsMakeRoomFor),根据情况确定是否扩容。流程如下:

  1. 检查buf数组种剩余空间avail是否大于需要添加的长度addlen:
    是:无需扩容,直接返回
    否:需要扩容,转至2

  2. 检查拼接后长度是否大于1MB:
    是:扩容长度=1MB+拼接后长度
    否:扩容长度=2*(拼接后长度)

  3. 根据新长度重新选取存储类型,分配空间,统计值赋值
    新长度类型不变:realloc申请内存空间(扩大buf数组)
    改变:重新开辟内存空间,将原buf的内容移动至新空间

在这里插入图片描述

本文参考《Redis5 设计与源码分析》

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值