Redis 简介

REmote DIctionary Server(Redis) 是一个由SalvatoreSanfilippo写的key-value存储系统。

Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种语言的API。

它通常被称为数据结构服务器,因为值(value)可以是字符串(String), 哈希(Map), 列表(list), 集合(sets) 和有序集合(sorted sets)等类型。

Redis特点

Redis 是完全开源免费的,遵守BSD协议,是一个高性能的key-value数据库。

Redis 与其他 key - value 缓存产品有以下三个特点:

  • Redis支持数据的持久化,可以将内存中的数据保持在磁盘中,重启的时候可以再次加载进行使用。

  • Redis不仅仅支持简单的key-value类型的数据,同时还提供list,set,zset,hash等数据结构的存储。

  • Redis支持数据的备份,即master-slave模式的数据备份。

Redis 优势

性能极高 – Redis能读的速度是110000次/s,写的速度是81000次/s 。

丰富的数据类型 – Redis支持 Strings, Lists, Hashes, Sets 及 Ordered Sets 数据类型操作。

原子 – Redis的所有操作都是原子性的,同时Redis还支持对几个操作全并后的原子性执行。

丰富的特性 – Redis 还支持 publish/subscribe, 队列,key 过期等等特性。

Redis对象类型简介

  • Redis是一种key/value型数据库,其中,每个key和value都是使用对象表示的。
    比如,我们执行以下代码:
redis> SET message "hello redis"

其中的key是message,是一个包含了字符串"message"的对象。而value是一个包含了"hello redis"的对象。
Redis共有五种对象的类型,分别是:

类型常量 对象的名称
REDIS_STRING 字符串对象
REDIS_LIST 列表对象
REDIS_HASH 哈希对象
REDIS_SET 集合对象
REDIS_ZSET 有序集合对象

Redis中的一个对象的结构体表示如下:

typedef struct redisObject {  // 类型  unsigned type:4;          // 编码方式  unsigned encoding: 4;  // 引用计数  int refcount;  // 指向对象的值  void *ptr;  } robj;

type表示了该对象的对象类型,即上面五个中的一个。但为了提高存储效率与程序执行效率,每种对象的底层数据结构实现都可能不止一种。encoding就表示了对象底层所使用的编码。

  • Redis对象底层数据结构
编码常量 编码所对应的底层数据结构
REDIS_ENCODING_INT long 类型的整数
REDIS_ENCODING_EMBSTR embstr 编码的简单动态字符串
REDIS_ENCODING_RAW 简单动态字符串
REDIS_ENCODING_HT 字典
REDIS_ENCODING_LINKEDLIST 双端链表
REDIS_ENCODING_ZIPLIST 压缩列表
REDIS_ENCODING_INTSET 整数集合
REDIS_ENCODING_SKIPLIST 跳跃表和字典
  • 字符串对象

字符串对象的编码可以是int、raw或者embstr
如果一个字符串的内容可以转换为long,那么该字符串就会被转换成为long类型,对象的ptr就会指向该long,并且对象类型也用int类型表示。
普通的字符串有两种,embstr和raw。embstr应该是Redis 3.0新增的数据结构,在2.8中是没有的。如果字符串对象的长度小于39字节,就用embstr对象。否则用传统的raw对象。

#define REDIS_ENCODING_EMBSTR_SIZE_LIMIT 44
robj *createStringObject(char *ptr, size_t len) {  if (len <= REDIS_ENCODING_EMBSTR_SIZE_LIMIT)  return createEmbeddedStringObject(ptr,len);  else  return createRawStringObject(ptr,len);
}

embstr的好处有如下几点:

  1. embstr的创建只需分配一次内存,而raw为两次(一次为sds分配对象,另一次为objet分配对象,embstr省去了第一次)。
  2. 相对地,释放内存的次数也由两次变为一次。
  3. embstr的objet和sds放在一起,更好地利用缓存带来的优势。

raw和embstr的区别可以用下面两幅图所示:

  • 列表对象
    列表对象的编码可以是ziplist或者linkedlist
  1. ziplist是一种压缩链表,它的好处是更能节省内存空间,因为它所存储的内容都是在连续的内存区域当中的。当列表对象元素不大,每个元素也不大的时候,就采用ziplist存储但当数据量过大时就ziplist就不是那么好用了。因为为了保证他存储内容在内存中的连续性,插入的复杂度是O(N),即每次插入都会重新进行realloc。如下图所示,对象结构中ptr所指向的就是一个ziplist整个ziplist只需要malloc一次,它们在内存中是一块连续的区域。

  1. linkedlist是一种双向链表。它的结构比较简单,节点中存放pre和next两个指针,还有节点相关的信息。当每增加一个node的时候,就需要重新malloc一块内存。

  • 哈希对象
    哈希对象的底层实现可以是ziplist或者hashtable。
    ziplist中的哈希对象是按照key1,value1,key2,value2这样的顺序存放来存储的。当对象数目不多且内容不大时,这种方式效率是很高的。

hashtable的是由dict这个结构来实现的, dict是一个字典,其中的指针dicht ht[2] 指向了两个哈希表

typedef struct dict {  dictType *type;  void *privdata;  dictht ht[2];  long rehashidx; /* rehashing not in progress if rehashidx == -1 */  int iterators; /* number of iterators currently running */
} dict;
typedef struct dictht {  dictEntry **table;  unsigned long size;  unsigned long sizemask;  unsigned long used;
} dictht;

dicht[0] 是用于真正存放数据,dicht[1]一般在哈希表元素过多进行rehash的时候用于中转数据。
dictht中的table用语真正存放元素了,每个key/value对用一个dictEntry表示,放在dictEntry数组中。

  • 集合对象
    集合对象的编码可以是intset或者hashtable
    intset是一个整数集合,里面存的为某种同一类型的整数,支持如下三种长度的整数:
#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_ENC_INT64 (sizeof(int64_t))

intset是一个有序集合,查找元素的复杂度为O(logN),但插入时不一定为O(logN),因为有可能涉及到升级操作。比如当集合里全是int16_t型的整数,这时要插入一个int32_t,那么为了维持集合中数据类型的一致,那么所有的数据都会被转换成int32_t类型,涉及到内存的重新分配,这时插入的复杂度就为O(N)了。
intset不支持降级操作。

  • 有序集合对象
    有序集合的编码可能两种,一种是ziplist,另一种是skiplist与dict的结合。
    ziplist作为集合和作为哈希对象是一样的,member和score顺序存放。按照score从小到大顺序排列
    skiplist是一种跳跃表,它实现了有序集合中的快速查找,在大多数情况下它的速度都可以和平衡树差不多。但它的实现比较简单,可以作为平衡树的替代品。它的结构比较特殊。下面分别是跳跃表skiplist和它内部的节点skiplistNode的结构体:
/* * 跳跃表 */
typedef struct zskiplist {  // 头节点,尾节点  struct zskiplistNode *header, *tail;  // 节点数量  unsigned long length;  // 目前表内节点的最大层数  int level;
} zskiplist;
/* ZSETs use a specialized version of Skiplists */
/* * 跳跃表节点 */
typedef struct zskiplistNode {  // member 对象  robj *obj;  // 分值  double score;  // 后退指针  struct zskiplistNode *backward;  // 层  struct zskiplistLevel {  // 前进指针  struct zskiplistNode *forward;  // 这个层跨越的节点数量  unsigned int span;  } level[];
} zskiplistNode;

head和tail分别指向头节点和尾节点,然后每个skiplistNode里面的结构又是分层的(即level数组)
用图表示,大概是下面这个样子:

总结

以上简单介绍了Redis的简介,特性以及五种对象类型和五种对象类型的底层实现。事实上,Redis的高效性和灵活性正是得益于同一个对象类型采用不同的底层结构,并且在必要的时候对二者进行转换,还有就是各种底层结构对内存的合理利用。

Worktile官网: https://worktile.com/

本文作者:Worktile高级工程师 龚林杰
文章首发于「Worktile官方博客」,转载请注明来源。

Redis 概念以及底层数据结构相关推荐

  1. Redis原理以及底层数据结构初探

    什么是Redis? 非关系型的键值对数据库,可以根据键以O(1)的时间复杂度取出或插入关联值 Redis的数据是存在内存中的 键值对中键的类型可以是字符串,整型,浮点型等,且键是唯一的 键值对中的值类 ...

  2. 一文带你深入理解Redis中的底层数据结构,再也不怕不懂数据类型的底层了

    数据结构前言 都说Redis快,因为什么呢?只是因为它是内存数据库,所有操作都是基于内存进行的吗?其实不然,这与它的数据结构也是密不可分的.下面我们就来了解一下Redis的数据结构. Redis 数据 ...

  3. Redis进阶-string底层数据结构精讲

    文章目录 Pre string 字符串 字符串的实现 字符串 内部结构 embstr vs raw Pre Redis进阶-核心数据结构进阶实战 Redis 有 5 种基础数据结构,分别为:strin ...

  4. Redis进阶-List底层数据结构精讲

    文章目录 Pre list 列表 队列 O(1) 栈 O(1) 查询 O(n) 快速列表 quicklist 压缩列表 ziplist ziplist 源码 entry 增加元素 快速列表 quick ...

  5. Redis第六讲 Redis之List底层数据结构实现

    List数据结构 List是一个有序(按加入的时序排序)的数据结构,Redis采用quicklist(双端链表) 和 ziplist 作为List的底层实现.可以通过设置每个ziplist的最大容量, ...

  6. Redis六种底层数据结构

    文章目录 一.简单动态字符串 1.SDS的结构定义 2.SDS和c字符串的区别 1)SDS获取字符串长度复杂度为常数 2)SDS杜绝了缓冲区溢出 3)减少内存重分配次数 4)二进制安全 5)SDS兼容 ...

  7. Redis底层数据结构介绍

    文章目录 前言 1. 哈希表 2. 简单动态字符串 使用SDS的好处 对比C字符串 3. 压缩列表 4. 跳表 5. 整数集合.双向链表 Redis数据类型与底层数据结构对照表 前言 一谈到Redis ...

  8. Redis从入门到精通之底层数据结构快表QuickList详解

    文章目录 0.前言 1. 快表的结构 2. Redis 6.0 快表quicklist 基本结构 2.1 成员变量 2.1 主要操作 2.1 推导结果 3. 快表的操作 3. 快表的优缺点 3.1 优 ...

  9. redis底层数据结构 -String

    redis包含5种常用数据结构 String .List.Hash.Set .Zset 基础铺垫 以set为例 redis其实可以理解为 K-V数据库,因此对每个键值对都会有一个 dictEntry, ...

最新文章

  1. 计算机一级c基础知识,计算机一级考试MSOffice基础试题
  2. 计算机教室内网连接不了,校园网登陆不了内网怎么办?校园网登陆不了内网的解决方法...
  3. 欢迎来到我的第一个个人laravel尝试论坛项目,给予评价和建议 谢谢。
  4. 在服务器使用mysql_Linux服务器---使用mysql
  5. 怎么才能点一下excel中的超链接就显示出图片?_Excel如何批量建立超链接,搭建工作台...
  6. python训练模型太大怎么处理_趣味Python之如何降低过拟合风险
  7. c语言求e近似值精度在10e6,中石油2013秋季学期《C语言》补考在线适用于2014年4月...
  8. Window mobile 实现CLIST始终选中最新添加数据
  9. 清明小长假不无聊:分享一大波影视网站和APP,轻松看全网视频
  10. linux软件安装rar,Rar for Linux的安装方法
  11. 空间统计分析-GeoDa软件
  12. 【解决】简单有效的使用lodop打印小票功能
  13. debian or ubuntu下 anjuta配置
  14. talib安装error: Microsoft Visual C++ 14.0 or greater is required. Get it with Microsoft C++ Build的解决方案
  15. 自动控制系统中的典型环节
  16. Android接口调用
  17. learn.log - 进程管理器fastcgi原理及fastcgi_param详解
  18. OHIF记录(二)——Viewers和React-vtk工具包互联
  19. fractions -- 分数
  20. 使用STM32CubeMX 快速生成 USB HID 工程 - STM32F107VCT6

热门文章

  1. python电影数据分析的代码_python-small-examples
  2. bind merge r 和join_R语言数据合并
  3. centos php svn,centos7中搭建svn服务器(示例代码)
  4. mybatis源码阅读
  5. 使用JUnit进行单元测试
  6. BZOJ 2707: [SDOI2012]走迷宫 [高斯消元 scc缩点]
  7. PHP中foreach详细分析—一般数组与对象数组
  8. 9款精致HTML5/jQuery日历时钟控件源码下载(源码请见百度云) 链接:http://pan.baidu.com/s/1geIXe75 密码:7m4a...
  9. DreamWeaver文件保存时,提示发生共享违例问题的解决方法
  10. 通用分页存储过程(转自邹建)