Rides:五种存储数据类型在底层的实现方式

本文深入解析Redis中五种核心数据结构:简单动态字符串(SDS)、双向链表、字典、跳跃表和整数集合。SDS提供字符串安全、高效操作;链表适用于列表键,提供节点重排与顺序访问;字典用于键值对存储,解决哈希冲突;跳跃表实现有序集合,高效查找;整数集合节省内存,灵活存储整数。
摘要由CSDN通过智能技术生成

目录

1.引言:

2、简单动态字符串(simple dynamic string)SDS       <=======>      STRING

2.1 概述

2.2  SDS 的定义

2.3  SDS 与 C 字符串的区别

3、双向链表  <=====>  列表键

3.1 概述

3.2 链表的数据结构

3.3 链表的特性

4、字典

4.1 概述

4.2 字典的定义

4.3 解决哈希冲突

4.4 Rehash

 

 5、跳跃表

  5.1 概述

  5.2 跳跃表的定义

  5.3 总结

 6、整数集合(Intset)

  6.1 概述

  6.2 整数集合的实现 

  6.3 整数集合的升级

  6.4 总结

7、压缩列表

  7.1 概述

  7.2 压缩列表的构成


1.引言:

Redis 是一个基于键值对(key-value)的分布式存储系统,与Memcached类似,却优于Memcached的一个高性能的key-value数据库。

《Redis设计与实现》这样描述:

    Redis 数据库里面的每个键值对(key-value) 都是由对象(object)组成的:

      数据库键总是一个字符串对象(string object);

      数据库的值则可以是字符串对象、列表对象(list)、哈希对象(hash)、集合对象(set)、有序集合(sort set)对象这五种对象中的其中一种。

 

    我们为什么会说Redis 优于Memcached 呢,因为Redis 的出现,丰富了memcached 中key-value的存储不足,在部分场合可以对关系数据库起到很好的补充作用,而且这些数据类型都支持push/pop、add/remove及取交集并集和差集及更丰富的操作,而且这些操作都是原子性的。

    

    我们今天探讨的并不是Redis 中value 的数据类型,而是他们的具体实现——底层数据类型

    Redis 底层数据结构有一下数据类型:

2、简单动态字符串(simple dynamic string)SDS       <=======>      STRING


2.1 概述

   Redis 没有直接使用C语言传统的字符串表示,而是自己构建了一种名为简单动态字符串(simple dynamic string SDS)的抽象类型,并将SDS用作Redis 的默认字符串表示:

例子:

redis>SET msg "hello world"
OK

   设置一个key= msg,value = hello world 的新键值对,他们底层是数据结构将会是:

     键(key)是一个字符串对象,对象的底层实现是一个保存着字符串“msg” 的SDS;

     值(value)也是一个字符串对象,对象的底层实现是一个保存着字符串“hello world” 的SDS

除了用来保存字符串以外,SDS还被用作缓冲区(buffer)AOF模块中的AOF缓冲区。

2.2  SDS 的定义

  Redis 中定义动态字符串的结构:

/*  
 * 保存字符串对象的结构  
 */  
struct sdshdr {  
    int len;  // buf 中已占用空间的长度 
    int free;  // buf 中剩余可用空间的长度
    char buf[];  // 数据空间  
};  

   

  1. len 变量,用于记录buf 中已经使用的空间长度(这里指出Redis 的长度为5)
  2. free 变量,用于记录buf 中还空余的空间(初次分配空间,一般没有空余,在对字符串修改的时候,会有剩余空间出现)
  3. buf 字符数组,用于记录我们的字符串(记录Redis)

2.3  SDS 与 C 字符串的区别

    传统的C 字符串 使用长度为N+1 的字符串数组来表示长度为N 的字符串,这样做在获取字符串长度,字符串扩展等操作的时候效率低下。C 语言使用这种简单的字符串表示方式,并不能满足Redis 对字符串在安全性、效率以及功能方面的要求。

2.3.1 获取字符串长度(SDS O(1)/C 字符串 O(n))

     传统的C 字符串 使用长度为N+1 的字符串数组来表示长度为N 的字符串,所以为了获取一个长度为C字符串的长度,必须遍历整个字符串。

     和C 字符串不同,SDS 的数据结构中,有专门用于保存字符串长度的变量,我们可以通过获取len 属性的值,直接知道字符串长度。

    

2.3.2 杜绝缓冲区溢出

    C 字符串 不记录字符串长度,除了获取的时候复杂度高以外,还容易导致缓冲区溢出。

     假设程序中有两个在内存中紧邻着的 字符串 s1 和 s2,其中s1 保存了字符串“redis”,二s2 则保存了字符串“MongoDb”:

      

     如果我们现在将s1 的内容修改为redis cluster,但是又忘了重新为s1 分配足够的空间,这时候就会出现以下问题:

      

      我们可以看到,原本s2 中的内容已经被S1的内容给占领了,s2 现在为 cluster,而不是“Mongodb”。

     Redis 中SDS 的空间分配策略完全杜绝了发生缓冲区溢出的可能性:

     当我们需要对一个SDS 进行修改的时候,redis 会在执行拼接操作之前,预先检查给定SDS 空间是否足够,如果不够,会先拓展SDS 的空间,然后再执行拼接操作

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

  C语言字符串在进行字符串的扩充和收缩的时候,都会面临着内存空间的重新分配问题。

   1. 字符串拼接会产生字符串的内存空间的扩充,在拼接的过程中,原来的字符串的大小很可能小于拼接后的字符串的大小,那么这样的话,就会导致一旦忘记申请分配空间,就会导致内存的溢出。

   2. 字符串在进行收缩的时候,内存空间会相应的收缩,而如果在进行字符串的切割的时候,没有对内存的空间进行一个重新分配,那么这部分多出来的空间就成为了内存泄露。

  举个例子:我们需要对下面的SDS进行拓展,则需要进行空间的拓展,这时候redis 会将SDS的长度修改为13字节,并且将未使用空间同样修改为1字节 

  

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值