2019独角兽企业重金招聘Python工程师标准>>>

redis是一个key-value储存系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)

redis字符串:在redis-Client中执行以下命令: SET USER_NAME zhangsan   会创建一个key为USER_NAME,value为zhangsan 的键值对。那么,那么这个字符串的值 "zhangsan" 在数据库中是以哪种数据结构储存的呢?

Redis对象

redis并没有直接只用string list set等来直接实现键值对数据库,而是根据这些数据结构创建了一个对象系统,这个系统包含了redis中五种数据结构。并且redis对象中记录最后一次访问时间,服务器会根据这个时间删除对象,从而释放内存(如果启用了maxmemory功能的情况下).

redisObject数据结构分析:

typedef struct redisObject {unsigned type:4;unsigned encoding:4;unsigned lru:REDIS_LRU_BITS; /* lru time (relative to server.lruclock) */int refcount;void *ptr;
} robj;

type占用4个bit位,记录了对象的类型 REDIS_STRING 0,REDIS_LIST 1,REDIS_SET 2,REDIS_ZSET 3,REDIS_HASH 4

ptr:该指针执行对象底层实现数据结构,这些数据结构对象由encoding属性决定。

encoding属性记录了对象所使用的编码,也就是说对象使用了什么数据结构作为对象底层实现。

可以这样理解:type记录的数据结构是针对redis使用者来说的。encoding记录的数据结构是redis底层的具体实现。并且每种类型的对象redis至少两种不同的编码,也就是两种不同的实现方式。使用 OBJECT ENCODING命令来查看数据库键的值对象编码。

可以看到“zhangsan”的编码是embstr.

字符串对象:

字符串对象的编码可以是int ,raw 或者embstr.如果一个字符串对象保存的是整数值,并且这个整数值可以用long类型标识,那么字符串对象将以整数值保存在ptr属性里面,并将字符串对象的编码设置为INT。

如果对"zhang_san"字符串进行修改,类型会变成raw

为什么对字符串进行更改时,会变成raw类型呢,emptr类型与raw类型有什么区别呢,接下类针对这两个具体类型进行详细描述。

embstr编码是为专门保存短字符串的一种编码方式,这种编码和raw编码一样,都使用redisObject结构和sdshdr结构来标识字符串对象,但raw编码会调用两次内存分配函数来创建redisObject结构和sdshdr结构,而embstr编码则通过调用一次内存分配函数来分配一块连续的空间,空间中一次包含redisObject和sdshdr两个结构。

接下来介绍sdshdr数据结构。

在c语言中,传统的字符串以空字符结尾的字符数组来表示,redis并没有直接使用这种传统的字符串标识,而是自己构建了一种名为(simple dynamid string ,SDS)的抽象类型,并将SDS用作Redis默认字符串表示。在redis中c字符串只会用在一些无需对字符串值修改的地方。


typedef char *sds;
struct sdshdr {int len;int free;char buf[];
};

其中len表示buf中已占用空间的长度,free中表示buf中可剩余空间的长度,buf是数据空间,如下图所示。

free属性值为0,表示这个SDS没有分配任何未使用空间,len为5表示这个SDS保存了一个五个字节的字符串,buf为一个char类型的数组,数组的前五个字节分别保存了'H','E','L','L','O'五个字符,最后一个字节则保存了空字符串\0.

SDS字符串与C字符串的区别。

C语言使用长度为N+1的字符数组来表示长度为N的字符串,并且字符数组的最后一个元素总是空字符'\0'.(java也是用的char[])

获取字符串的长度,对c语言字符串来说,c字符串并不记录自身的长度,获取一个c字符串的长度必须遍历整个字符串,这个操作的复杂度为O(n)的。而redis的STRLEN命令的复杂度仅为O(1).

减少内存分配的次数,c字符串并不记录自身长度,因此每次增长或者缩短一个字符串时,都需要对内存进行一次内存重新分配的操作。

对于redis中sds来说会进行空间预分配:(可以参考JAVA 中 ArrayList这种数据结构的扩容,他们是类似的。ArrayList没记错的话是每次扩1.5倍)

sds sdsMakeRoomFor(sds s, size_t addlen) {struct sdshdr *sh, *newsh;// 获取sds目前空闲的长度size_t free = sdsavail(s);size_t len, newlen;// 空间足够,则直接返回,不重新分配if (free >= addlen) return s;// 获取 s 目前已占用空间的长度len = sdslen(s);sh = (void*) (s-(sizeof(struct sdshdr)));// s 最少需要的长度newlen = (len+addlen);// 根据新长度,为 s 分配新空间所需的大小if (newlen < SDS_MAX_PREALLOC)// 如果新长度小于 SDS_MAX_PREALLOC // 那么为它分配两倍于所需长度的空间newlen *= 2;else// 否则,分配长度为目前长度加上 SDS_MAX_PREALLOCnewlen += SDS_MAX_PREALLOC;// T = O(N)newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+1);// 内存不足,分配失败,返回if (newsh == NULL) return NULL;// 更新 sds 的空余长度newsh->free = newlen - len;// 返回 sdsreturn newsh->buf;
}

同时sds是惰性空间释放,当sds字符串缩短操作时不会立即free()空间,而是使用free属性将这些字节的数量记录起来,并等待将来使用。

C字符串中的字符必须符合某种编码,比如(ASCII),并且出了字符串末尾之外,字符串里不能包含空字符,否则会认为已经到结尾,因此c字符串只能保存文本数据,而不能保存图片,音频,视频等二进制数据。

同时c字符串提供的api是不安全的,比如在修改内容时忘记分配空间。

总结:

C字符串 SDS
获取字符串长度复杂度为O(n) 获取字符串长度复杂度为O(1)
API是不安全的,可能造成缓冲区溢出 API是安全的,可能造成缓冲区溢出
修改N次字符串必然进行N次内存分配 修改N次字符串最多进行N次内存分配
只能保存文本数据 可以保存文本数据和二进制数据

对SET key HELLO 命令执行后,内部储存结构示意图。

参考资料:

<C语言程序设计>

<Redis设计与实现>

Redis源码:https://github.com/antirez/redis/tree/unstable/src

---------------------------分割线----------------------------

看了redis源码才知道,原来c语言还能这样封装!

转载于:https://my.oschina.net/wang520/blog/2644246

redis数据结构分析-redisObject-SDS相关推荐

  1. 【Redis数据结构篇】- SDS

    目录 前言 一. 数据结构 二 . 特点 2.1 二进制安全 2.2 内存空间分配 空间预分配 惰性回收 2.3 总结与C字符串的区别: 前言 Redis虽然是使用C语言编写的,但是并没有使用C语言的 ...

  2. 聊一聊 Redis 数据内部存储使用到的数据结构

    点击上方 好好学java ,选择 星标 公众号 重磅资讯.干货,第一时间送达 今日推荐:2020年7月程序员工资统计,平均14357元,又跌了,扎心个人原创100W+访问量博客:点击前往,查看更多 R ...

  3. java使用xml存储数据_聊一聊 Redis 数据内部存储使用到的数据结构

    Redis 数据库虽然一直都在使用,但是对其内部存储结构之类的,都没有研究过,哪怕是面试的时候都没有准备过这方面的东西.最近在看一门网课,里面有讲到过这一块的内容,结合了<Redis 设计与实现 ...

  4. Redis数据的类型

    Redis一共分为五种基本数据类型:String.Hash.List.Set.Zset. string 内部编码有三种,raw,embstr,int String 是二进制的.可以存储序列化对象,图片 ...

  5. 【Redis系列2】Redis字符串对象之SDS(简单动态字符串)实现原理分析

    Redis字符串对象之SDS实现原理分析 前言 字符串对象 为什么Redis的字符串对象是二进制安全的 SDS空间分配策略 空间预分配 惰性空间释放 SDS和C语言字符串区别 SDS的底层存储对象 d ...

  6. Redis数据实战之GEO在LBS中应用与自定义新数据类型

    Redis数据实战之GEO在LBS中应用与自定义新数据类型 引言 面向 LBS 应用的 GEO 数据类型 GEO 的底层结构 GeoHash 的编码方法 如何操作 GEO 类型 如何自定义数据类型 R ...

  7. Redis数据持久化机制AOF原理分析一---转

    http://blog.csdn.net/acceptedxukai/article/details/18136903 http://blog.csdn.net/acceptedxukai/artic ...

  8. 关于Redis数据过期策略

    前言 在项目中某场景下,需要频繁去设置redis数据的过期时间,因此去了解了下redis数据过期策略.原文地址:关于Redis数据过期策略 一.Redis中key的的过期时间 通过EXPIRE key ...

  9. Redis数据持久化机制AOF原理分析二

    本文所引用的源码全部来自Redis2.8.2版本. Redis AOF数据持久化机制的实现相关代码是redis.c, redis.h, aof.c, bio.c, rio.c, config.c 在阅 ...

  10. 阿里云Redis数据过期和淘汰策略解答

    背景 阿里云Redis作为一个高性能的内存NoSQL数据库,其容量受到最大内存限制的限制. 用户在使用阿里云Redis时,除了对性能,稳定性有很高的要求外,对内存占用也比较敏感.在使用过程中,有些用户 ...

最新文章

  1. android 读取内部存储文件格式,Android中的数据储存之文件存储
  2. 使用ntpdate校正linux系统的时间
  3. rsyslog服务日志报错分析1
  4. python是动态类型语言、变量不需要显示声明类型_【IT专家】第3章 Python基础
  5. 吴恩达的2021回顾,这些大事件影响了AI这一年
  6. 原始套接字抓取所有以太网数据包与分析
  7. 20天掌握C语言,C语言零基础到项目实战,玩转C语言
  8. 2021辽宁高考艺考成绩查询系统入口,2019年辽宁省艺考统考成绩查询官方入口
  9. 国科大杨力祥老师操作系统答案总结
  10. 云课堂智慧职教答案python_云课堂智慧职教答案python,云课堂智慧职教数学答案,云课堂智慧职教搜题...
  11. 【图论】Graph Fourier Transform
  12. 关于mysql的表情包_mysql表情包 - mysql微信表情包 - mysqlQQ表情包 - 发表情 fabiaoqing.com...
  13. 联想移动裁员为求自保 摩托罗拉品牌逐渐消退
  14. android的NDK安装及工程实例
  15. Graph U-Nets [gPool gUnpool] 图分类 节点分类 图池化 ICML 2019
  16. 运放专题:运放输出电压
  17. ios与android指纹识别,iOS开发实现TouchID指纹解锁
  18. 封杀太愚蠢,马斯克喊话解封特朗普推特账号!
  19. 如何寻找基因的启动子
  20. 关于CSAPP的学习:如何与如同机翻的文字搏斗及如何快速理解冗长的说明

热门文章

  1. 万邦淘宝/天猫按关键字搜索淘宝商品 API 返回值
  2. 59% 的程序员曾担心自己猝死!
  3. c语言折半查找递归程序,用递归法编写一个函数fac,求n!的值
  4. #01 Linear Regression Excise
  5. c语言逗号分隔字符串,[数字用逗号隔开怎么读]看到一个数字中间有逗号
  6. win10虚拟机搭建群晖nas碰到的一些问题
  7. visio粘贴excel图表
  8. 015-lissajous server
  9. 【Web安全从入门到放弃】06_文件包含漏洞
  10. MySQL数据库(一)服务器数据库的搭建和远程访问