背景

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. Java_转换流和缓冲流

    今日内容介绍 转换流 缓冲流 1 转换流 在学习字符流(FileReader.FileWriter)的时候,其中说如果需要指定编码和缓冲区大小时,可以在字节流的基础上,构造一个InputStreamR ...

  2. 【转】解决eclipse连接不到genymotion的问题

    (1)很多朋友在使用genymotion开发安卓应用程序的时候,会遇见完全正确的安装但是在运行的时候仍然找不到,genymotion上的设备,在打开的devices上找不到如下图所示: (2)解决的方 ...

  3. 关于FFT分析音频的学习

    本文部分知识从以下文章学习: https://zhuanlan.zhihu.com/p/19763358 傅里叶变换的知识 https://www.cnblogs.com/RabbitHu/p/FFT ...

  4. Thrift框架快速入门

    Thrift介绍1.什么是thrift?thrift早期由facebook内部团队开发,主要用于实现跨语言间的方法调用,属于远程方法调用的一种,后开源纳入apache中,成为了apache thrif ...

  5. 记录一下我做Udacity 的Data Scientist Nano Degree Project

    做项目的时候看了别人的blog,决定自己也随手记录下在做项目中遇到的好的小知识点. 最近在做Udacity的Data Scientist Nano Degree Project的Customer_Se ...

  6. 90后iOS开发者的出路,如何规划30岁前的自己(程序员必修课)

    前言: 最近发生了一些和我们没有直接关系但是有间接关系的事情.比如华为“清洗”高龄基层员工,比如游戏公司2号员工拿不到股份而离职.先不说事实到底如何,起码很多码农是心有戚戚焉. 最近一年多也发生了一些 ...

  7. Spring aop 拦截自定义注解+分组验证参数

    import com.hsq.common.enums.ResponseState;import com.hsq.common.response.ResponseVO;import org.aspec ...

  8. 3PHP如何用PDO的连接方式方式导出mysql数据

    首先连接mysql,具体看上一篇 接下来在try{}中加入以下代码 $query="select * from 你的数据表名称"          //$query的内容给个SQL ...

  9. 转载 | Sublime text3 实用快捷键整理

    实用快捷键 Ctrl+Shift+P:打开命令面板Ctrl+P:搜索项目中的文件Ctrl+G:跳转到第几行Ctrl+W:关闭当前打开文件Ctrl+Shift+W:关闭所有打开文件Ctrl+Shift+ ...

  10. HTML/CSS:block,inline和inline-block概念和区别

    总体概念 block和inline这两个概念是简略的说法,完整确切的说应该是 block-level elements (块级元素) 和 inline elements (内联元素).block元素通 ...