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" 在数据库中是以哪种数据结构储存的呢?

RedisObject对象

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

  redisObject数据结构分析:

typedef struct redisObject {
unsigned type:;
unsigned encoding:;
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两个结构

              

SDS数据结构

  在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 *= ;
else
// 否则,分配长度为目前长度加上 SDS_MAX_PREALLOC
newlen += SDS_MAX_PREALLOC;
// T = O(N)
newsh = zrealloc(sh, sizeof(struct sdshdr)+newlen+); // 内存不足,分配失败,返回
if (newsh == NULL) return NULL; // 更新 sds 的空余长度
newsh->free = newlen - len; // 返回 sds
return 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 命令执行后,内部储存结构示意图。

          

redis数据结构分析 (redisObject、SDS)的更多相关文章

  1. redis数据存储的细节

    redis是一个K-V NoSql非关系型数据库,redis有物种数据类型,分别是String,Hash,list,set,zset:这五种类型都是针对K-V中的V设计的. 1.总体介绍:关于redi ...

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

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

  3. redis源码之SDS

    1:SDS介绍 我们在redis中执行命令 set key name 的时候,key和name都是字符串类型,而且字符串(string)在redis中是会经常用到的类型,那redis是如何保存字符串的 ...

  4. Redis数据持久化—RDB持久化与AOF持久化

    目录 Redis数据持久化-RDB持久化与AOF持久化 RDB持久化 RDB文件的创建 RDB文件的载入 自动间隔性保存 检查保存条件是否满足 AOF持久化 AOF持久化的实现 AOF文件的载入与数据 ...

  5. Redis数据导入工具优化过程总结

    Redis数据导入工具优化过程总结 背景 使用C++开发了一个Redis数据导入工具 从oracle中将所有表数据导入到redis中: 不是单纯的数据导入,每条oracle中的原有记录,需要经过业务逻 ...

  6. 5.1.1 读取Redis 数据

    Redis 服务器是Logstash 推荐的Broker选择,Broker 角色就意味会同时存在输入和输出两个插件. 5.1.1 读取Redis 数据 LogStash::Input::Redis 支 ...

  7. Redis数据备份和重启恢复

    一.对Redis持久化的探讨与理解 目前Redis持久化的方式有两种: RDB 和 AOF 首先,我们应该明确持久化的数据有什么用,答案是用于重启后的数据恢复. Redis是一个内存数据库,无论是RD ...

  8. Redis数据迁移方案

    场景 Redis实例A ---> Redis实例B,整库全量迁移 方案一: mac环境 brew install npm npm install redis-dump -g 针对RedisA: ...

  9. Redis学习笔记(5)——Redis数据持久化

    出处http://www.cnblogs.com/xiaoxi/p/7065328.html 一.概述 Redis的强大性能很大程度上都是因为所有数据都是存储在内存中的,然而当Redis重启后,所有存 ...

随机推荐

  1. 展BOM

    CS_BOM_EXPL_MAT_V2 *&---------------------------------------------------------------------* *&am ...

  2. iOS定时器按钮短时间内多次点击只触发一次事件方法

    今天在看别人代码的时候,有个个60秒获取验证码的功能,做了个定时器,按钮触发定时器,点击按钮后设置按钮的enabled为NO,逻辑来讲都是没问题的 但是实际操作的时候,恶意的在短时间内多次点击那个获取 ...

  3. iOS技术面试05:UI控件

    怎么解决缓存池满的问题 1> 优先移除使用次数比较少的对象 2> 优先移除缓存时间最长的对象(让年轻的活下来) 3> 优先移除占用内存比较大的对象 CAAnimation的层级结构 ...

  4. 单点登录系统(SSO)详细设计说明书(上篇)

    1.引言 1.1编写目的 为了单点登录系统(SSO系统)的可行性,完整性,并能按照预期的设想实现该系统,特编写需求说明书.         同时,说明书也发挥与策划和设计人员更好地沟通的作用. 1.2 ...

  5. react做的简单的购物车

    ###第一步 :首先电脑上已经安装react的脚手架 cnpm  install    create-react-app   -g ###第二步 :创建项目 creact-react-app   项目 ...

  6. Direct2D 学习笔记(1)概述

    Direct2D 应用程序接口概述 资源网站 https://docs.microsoft.com/en-us/windows/win32/Direct2D/the-direct2d-api 主要用到 ...

  7. elasticsearch7.1.1【win】下载安装

    下载:https://www.elastic.co/cn/downloads/elasticsearch 历史版本下载:https://www.elastic.co/cn/downloads/past ...

  8. python线程事件Event(30)

    在python项目开发中,线程thread使用是比较常见的,在前面的文章中我们介绍了 python线程的创建 以及 线程互斥锁 ,今天还要额外介绍一个与线程相关的内容 – 事件Event. 一.pyt ...

  9. 微信公众号对接JS-SDK接口 调用微信内置地图

    微信js-sdk开发文档:https://developers.weixin.qq.com/doc/offiaccount/OA_Web_Apps/JS-SDK.html 1.页面配置 引用微信js- ...

  10. react生成二维码

    图片实例: 简介: QRCode.js 是一个生成二维码的JS库.主要是通过获取 DOM 的节点,再通过 HTML5 Canvas 绘制而成,不依赖任何库. 用法: 1. 在项目中引入qrcode.m ...