背景

redis功能强大,几乎已经成了现代大中型服务必备的缓存技术了。 除了十分给力的缓存功能,redis当做消息队列,数据库也有着不错的表现。

我们都知道,redis 有五种数据类型,string,list, hash, set 和zset。 其中 最基本的,同时也是最常用的 就是string了。 本文就来谈谈 redis内部,string 的实现原理:SDS(simple dynamic string)。

redis简单动态字符窜:SDS

  • 在redis里,C语言的字符窜只用来放字符串字面量,即只有当无序对字符串修改的时候才用C的字符串,例如打印日志的时候。
  • 除了基本的字符串存储之外,sds还用做缓冲区。AOF模块的缓冲区,和客户端状态的输入缓冲区,都是sds实现的。
SDS 定义
struct sdshdr {

    // buf 中已占用空间的长度
int len; // buf 中剩余可用空间的长度
int free; // 数据空间
char buf[];
};

图示如下:

  • 简单解释一下: buf是一个字节数组,是用来放具体数据的。其长度是按一定策略伸缩的,具体解释在下面。 len 表示buf 中已经使用掉的长度,free表示 buf中尚未使用的长度。

  • buf内 sds 的字符串,总是以空字符结尾,这一点同c字符串一致。 因此sds 可以直接重用一部分c字符串函数库的函数。

SDS 与C字符串的对比 和优点

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

  • 因为sds已经存了数据的长度,所以获取字符串长度复杂度为O(1),而C字符串获取长度为O(n)。

2,杜绝缓冲区溢出导致的内存问题

  • 假设内存区域有s1:“hi”,s2: “redis” 两个字符串,位置紧邻,如下图:

  • 此时需要给s1 追加一个“boy”, 如果是C字符串,忘记了在追加之前先给s1 分配空间,此时追加将导致 s2的值被意外的修改。 而使用 sds则不会有这个问题。 因为其封装好的函数,会在追加数据之前先检查 空间是否够用,如果不够用就扩容。

3,通过空间预分配和空间惰性释放 减少内存分配问题

  • 当给sds的值追加一个字符串,而当前的剩余空间不够时,就会触发sds的扩容机制。扩容采用了空间预分配的优化策略,即分配空间的时候:如果sds 值大小< 1M ,则增加一倍; 反之如果>1M , 则当前空间加1M作为新的空间。

  • 当sds的字符窜缩短了,sds的buf内会多出来一些空间,这个空间并不会马上被回收,而是暂时留着以防再用的时候进行多余的内存分配。这个是惰性空间释放的策略

4, 二进制安全

  • c字符串必须符合某种编码(例如ASCII),且不能包含空字符。 这些限制使得 c字符窜不能保存图片,音频等二进制文件。 而sds的api 都是二进制安全的,其所有api 都会以处理二进制的方式来处理buf内的数据,所以不会有任何的限制。

SDS 的API接口列表

函数 作用 复杂度
sdsnew 以一个c字符窜为参数新建sds O(N)
sdsempty 新建空的sds字符串 O(1)
sdsfree 释放sds O(N)
sdslen 获取已使用长度 O(1)
sdsavail 获取未使用长度 O(1)
sdsdup 创建一个sds的副本 O(N)
sdsclear 清空 O(1)
sdscat 追加C字符串到sds O(N)
sdscatsds 追加sds字符串到sds O(N)
sdscpy 用c字符串覆盖sds值 O(N)
sdsgrowzero 用空字符串扩展 sds至给定长度 O(N)
sdsrange 删除给定区间外的数据 O(N)
sdscmp 对比sds是否相同 O(N)
sdstrim 从sds中去除给定c字符串中出现过的字符 O(N*M)

总结

sds 其实就是字符串数组的一个封装,但是由于考虑了多种场景,作者给它适配了多个高效、优雅的接口,使得 sds成为了一个存储字符串的优秀设计。使得sds成为一个独立的、可提供高效优质服务的基础实体。

我们在设计一些偏底层的数据结构、对象、甚至是数据库表的时候,可以参考sds的设计,从中寻找一些启发。

参考: 《Redis 设计与实现》 黄健宏

有收获就点个赞吧~

redis 原理系列之--字符串存储的实现原理(1)的更多相关文章

  1. 🍃【Spring专题】「原理系列」SpringMVC的运行工作原理(补充修订)

    承接相关之前的SpringMVC的框架技术的流程分析 初始化流程(initStrategies) 执行流程 寻找相关HandlerMapping 请求到DispatcherServlet类进行执行相关 ...

  2. 分布式缓存技术redis学习系列(四)——redis高级应用(集群搭建、集群分区原理、集群操作)

    本文是redis学习系列的第四篇,前面我们学习了redis的数据结构和一些高级特性,点击下面链接可回看 <详细讲解redis数据结构(内存模型)以及常用命令> <redis高级应用( ...

  3. Redis字符串键的底层原理

    before C语言基础 Redis基础 导入 redis的命令如下: set x "hello"; get x; hello Redis作为一种存储字符串的缓存结构,其具体实现是 ...

  4. Redis核心原理与实践--字符串实现原理

    Redis是一个键值对数据库(key-value DB),下面是一个简单的Redis的命令: > SET msg "hello wolrd" 该命令将键"msg&q ...

  5. 分布式缓存技术redis学习系列(五)——redis实战(redis与spring整合,分布式锁实现)

    本文是redis学习系列的第五篇,点击下面链接可回看系列文章 <redis简介以及linux上的安装> <详细讲解redis数据结构(内存模型)以及常用命令> <redi ...

  6. Docker系列05—Docker 存储卷详解

    本文收录在容器技术学习系列文章总目录 1.存储卷介绍 1.1 背景 (1)docker 的 AFUS 分层文件系统 docker镜像由多个只读层叠加面成,启动容器时,docker会加载只读镜像层并在镜 ...

  7. Python操作redis学习系列之(集合)set,redis set详解 (六)

    # -*- coding: utf-8 -*- import redis r = redis.Redis(host=") 1. Sadd 命令将一个或多个成员元素加入到集合中,已经存在于集合 ...

  8. 分布式缓存技术redis学习系列(二)——详细讲解redis数据结构(内存模型)以及常用命令

    Redis数据类型 与Memcached仅支持简单的key-value结构的数据记录不同,Redis支持的数据类型要丰富得多,常用的数据类型主要有五种:String.List.Hash.Set和Sor ...

  9. 【深入ASP.NET原理系列】--ASP.NET页面生命周期

    前言 ASP.NET页面运行时候,页面将经历一个生命周期,在生命周期中将执行一系列的处理步骤.包括初始化.实例化控件.还原和维护状态.运行时间处理程序代码以及进行呈现.熟悉页面生命周期非常重要,这样我 ...

随机推荐

  1. javascript基础学习第三天

    ☞ 命名法: 小驼峰命名法 和 大驼峰命名法(帕斯卡命名法) 变量命名规则:遵循小驼峰命名法 [变量名第一个字母小写后面每一个单词的首字母大写] var userNameAge; 函数命名规则:遵循帕 ...

  2. http状态码 400-499

    类比 服务器:便利店 客户端:客人 http报文:中文语言+钱 400-499 客户的错误 400 :服务器不理解客服端请求的意思是什么,如请求报文损坏 举例: 客户端:@#!3&* 服务器: ...

  3. .net持续集成单元测试篇之单元测试简介以及在visual studio中配置Nunit使用环境

    系列目录 单元测试及测试驱动开发简介 什么是单元测试 单元测试是一段自动化的代码,这段代码调用被测试的工作单元,之后对这个单元的单个最终结果的某些假设进行检验.单元测试几乎都是用单元测试框架编写的.单 ...

  4. 警惕!CAF效应导致PCB漏电

    最近碰到一个PCB漏电的问题,起因是一款低功耗产品,本来整机uA级别的电流,常温老化使用了一段时间后发现其功耗上升,个别样机功耗甚至达到了mA级别.仔细排除了元器件问题,最终发现了一个5V电压点,在产 ...

  5. 03、Swagger2和Springmvc整合详细记录(爬坑记录)

    时间 内容 备注 2018年6月18日 基本使用 spirngmvc整合swagger2 开始之前这个系列博文基本是,在项目的使用中一些模块的内容记录,但是后期逐渐优化,不单单是整合内容. swagg ...

  6. 探秘最小生成树&&洛谷P2126题解

    我在这里就讲两种方法 Prim 和 Kruscal Kruscal kruscal的本质其实是 排序+并查集 ,是生成树中避圈法的推广 算法原理如下 (1)将连通带权图G=<n,m>的各条 ...

  7. 分布式ID系列(3)——数据库自增ID机制适合做分布式ID吗

    数据库自增ID机制原理介绍 在分布式里面,数据库的自增ID机制的主要原理是:数据库自增ID和mysql数据库的replace_into()函数实现的.这里的replace数据库自增ID和mysql数据 ...

  8. MySQL多表(理论知识总结)

    1. 多表关系    外键    foreign key    添加外键语法:    alter table 表名1 add foreign key(外键名称) references 表名2(主键名称 ...

  9. XML简单了解一下

    XML是一种纯文本文档.HTML,标记是已经被W3C规定好的,自己创建一个标签是不被允许的. XML现在的用途是用来存储数据.config文件就是个XML文档.XML是可以自定义的. 每一个XML文档 ...

  10. Python依赖包整体迁移方法

    1.新建site-packages目录,进入到site-packages目录下: 2.在site-packages目录下执行pip freeze >requirements.txt: 3.查看r ...