简介

redis[1]是一个key-value存储系统。和Memcached类似,它支持存储的value类型相对更多,包括string(字符串)、list(链表)、set(集合)、zset(sorted set --有序集合)和hash(哈希类型)。通常我们并不需要理解其底层数据结构,但如果能了解一下相关知识将会有助于我们更有效地使用Redis,并能够将这些知识应用到我们的工作中。

Redis内部实现如下数据结构[2,3,4,10]:

1 String

2 Hash Table

3 Doubly Linked List

4 Skip List

5 Zip List

6 Int Sets

7 Zip Maps (从2.6版本开始废弃)



Hast table[5]:

在Redis中,所有key-value对都存储在一个hash table中。这个Hash table是一个二维结构。其包括一个一维固定长度的数组,每个槽位上保存一个dictEntry对象。key计算hash值后按照这个定长数组求模,结果相同的key-balue通过链表保存在同一个槽位上,这样便形成了一个二维结构。需要说明的是,hash table中这个固定长度的数组能够根据key-value数量动态调整大小,细节说明请参看引用[5],这里不做更多说明。



这里再看一下dictEntry的定义,主要关注其中的联合体v的val成员:

struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
struct dictEntry *next;
} dictEntry;

val是一个类型为robj的数据结构,其中的type标示了当前的value的数据类型(即string、list、set、zset或者hash),encoding标示了当前value存储方式(即ziplist,String,hash table或者double linked list等)

struct redisObject {
unsigned type:4;
unsigned notused:2; /* Not used */
unsigned encoding:4;
unsigned lru:22; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr;
} robj;

encoding目前支持的范围如下所示,具体可参考[1]源代码实现,其中的zipmap由于表示范围的限制已经在2.6版本中废弃,相关说明参见[6]

#define REDIS_ENCODING_RAW 0        /* Raw representation */
#define REDIS_ENCODING_INT 1 /* Encoded as integer */
#define REDIS_ENCODING_HT 2 /* Encoded as hash table */
#define REDIS_ENCODING_ZIPMAP 3 /* Encoded as zipmap */
#define REDIS_ENCODING_LINKEDLIST 4 /* Encoded as regular linked list */
#define REDIS_ENCODING_ZIPLIST 5 /* Encoded as ziplist */
#define REDIS_ENCODING_INTSET 6 /* Encoded as intset */
#define REDIS_ENCODING_SKIPLIST 7 /* Encoded as skiplist */

五种数据类型的内部实现

Redis在收到客户端的请求后,为每一个参数创建一个robj对象,type定义为REDIS_STRING,encoding为REDIS_ENCODING_RAW。接下来Redis根据第一个robj对象(也就是命令名)查找对应的函数,并调用查找到的函数,命令执行过程可参考[7]。

String

如果一个String类型的value能够保存为整数,则将对应robj对象的encoding修改为REDIS_ENCODING_INT,将对应robj对象的ptr值改为对应的数值。如果不能转为整数,保持原有encoding为REDIS_ENCODING_RAW。

因此String类型的数据可能使用原始的字符串存储(实际为sds - Simple Dynamic Strings[9],对应encoding为REDIS_ENCODING_RAW)或者整数存储。

具体查看某一个key的encoding,参考Redis命令object[8]



下面是具体的例子:

redis 127.0.0.1:6379> set hello 1

OK

redis 127.0.0.1:6379> OBJECT ENCODING hello

"int"

redis 127.0.0.1:6379> set hello world

OK

redis 127.0.0.1:6379> OBJECT ENCODING hello

"raw"

List

List类型的key创建时使用zip list结构存储,robj对象的encoding字段设置为REDIS_ENCODING_ZIPLIST。zip list实现细节可参考[3]。概况来讲,zip list通过一个连续的内存块实现list结构,其中的每个entry节点头部保存前后节点长度信息,实现双向链表功能。这个头部可根据前后entry长度进行内存压缩,而如果直接使用指针的话则至少需要两个指针,对64位系统来说将占用16个字节,使用zip list时最好情况下只需要两个字节,这在具有大量list类型的key-value对且各个value较小的应用来说,可以节省大量内存。

当list的elem数小于配置值: hash-max-ziplist-entries 或者elem_value字符串的长度小于 hash-max-ziplist-value, 可以编码成 REDIS_ENCODING_ZIPLIST 类型存储,以节约内存;但由于在zip list添加和删除元素会涉及到数据移动,因此当list内容较多时,转而使用双向链表。双向链表的实现可参考数据结构相关教科书。

相关内存优化说明请参考[11]。

Hash

新建的Hash类型也使用ziplist存储value,保存数据过多时,转而使用hast table。

Set

创建Set类型的key-value时,如果value能够表示为整数,则使用intset类型保存value。intset使用和ziplist相似的实现方式保存整数[4]。数据量大时,切换为使用hash table保存各个value。

Zset

zset指排序的set,如果新建的zset包含value数大于配置或者value长度大于配置值[11],则直接使用hash table和skip list[12]存储value,skip list实现对value的排序;否则直接使用skip list存储value。Redis可以保存相同score的value值,其实现可参考源代码[1]以及文献[12],Redis是参考[12]中伪代码实现的。



本文只对Redis底层数据结构实现进行了简单归并汇总,各部分实现细节请参考引用链接即Redis源代码。本文内容基于Redis 2.6版本。

引用

[1] http://redis.io/

[2] http://stackoverflow.com/questions/9625246/what-are-the-underlying-data-structures-used-for-redis

[3] 《Redis ziplist内部结构分析》, http://www.searchdatabase.com.cn/showcontent_60781.htm

[4] 《解读Redis中ziplist、zipmap、intset实现细节》, http://www.wzxue.com/%E8%A7%A3%E8%AF%BBredis%E4%B8%ADziplist%E5%AE%9E%E7%8E%B0%E7%BB%86%E8%8A%82/

[5] 《redis源代码分析 – hash table》,http://www.kuqin.com/database/20110904/264306.html

[6] zipmap zmlen is too short, https://github.com/antirez/redis/issues/188

[7] 《深入理解Redis:命令处理流程 》, http://blog.csdn.net/hanhuili/article/details/17339005

[8] http://redis.io/commands/object

[9] 《Redis sds数据结构实现分析》,http://www.searchdatabase.com.cn/showcontent_64553.htm

[10] 《Redis内存存储结构分析》,http://www.searchtb.com/2011/05/redis-storage.html

[11] http://redis.io/topics/memory-optimization

[12] http://homepage.divms.uiowa.edu/~ghosh/skip.pdf

深入理解Redis:底层数据结构的更多相关文章

  1. Redis底层数据结构详解

    上一篇说了Redis有五种数据类型,今天就来聊一下Redis底层的数据结构是什么样的.是这一周看了<redis设计与实现>一书,现来总结一下.(看书总是非常烦躁的!) Redis是由C语言 ...

  2. Redis 底层数据结构介绍

    Redis 底层数据结构 版本:2.9 支持的数据类型: 字符串 散列 列表 集合 有序集合 字符串 Redis 利用原生的 c 字符串进行了一次封装.封装的字符串叫做简单动态字符串:SDS(simp ...

  3. 【redis】redis底层数据结构原理--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表等

    redis有五种数据类型string.list.hash.set.zset(字符串.哈希.列表.集合.有序集合)并且自实现了简单动态字符串.双端链表.字典.压缩列表.整数集合.跳跃表等数据结构.red ...

  4. Redis学习笔记(二)redis 底层数据结构

    在上一节提到的图中,我们知道,可以通过 redisObject 对象的 type 和 encoding 属性.可以决定Redis 主要的底层数据结构:SDS.QuickList.ZipList.Has ...

  5. redis底层数据结构--简单动态字符串 链表 字典 跳跃表 整数集合 压缩列表

    1.动态字符串 redis中使用c语言的字符床存储字面量,默认字符串存储采用自己构建的简单动态字符串SDS(symple dynamic string) redis包含字符串的键值对都是用SDS实现的 ...

  6. redis 底层数据结构 压缩列表 ziplist

    压缩列表是列表键和哈希键的底层实现之一.当一个列表键只包含少量列表项,并且每个列表项要么就是小整数,要么就是长度比较短的字符串,redis就会使用压缩列表来做列表键的底层实现 当一个哈希键只包含少量键 ...

  7. redis 底层数据结构 整数集合intset

    整数集合是集合键的底层实现之一,当一个集合只包含整数值元素,并且这个集合的元素数量不多时Redis就会使用整数集合作为集合键的底层实现 整数集合是Redis用于保存整数值的集合抽象数据结构,它可以保存 ...

  8. Redis底层数据结构实现

    REDIS  较宽泛的支持5种数据结构  分别为 字符串 列表 集合 散列 有序集合 关于这几种数据结构的使用 相信网上有很多资料,查看官网API 也很详细了  读者可以自己随意翻阅 很方便 . 接下 ...

  9. Redis 底层数据结构之压缩列表

    文章参考:<Redis 设计与实现>黄建宏 压缩列表 压缩列表 ziplist 是列表键和哈希键的底层实现之一.当一个列表键只包含少量列表项,并且每个列表项要么就是小整数值,要么就是长度比 ...

  10. Redis 底层数据结构之跳跃表

    文章参考 <Redis 设计与实现>黄建宏 Redis(2) 跳跃表 跳跃表 跳跃表 skiplist 是一种有序数据结构,它通过在每个节点中维持多个指向其他节点的指针,从而达到快速访问节 ...

随机推荐

  1. 直播未来属于RTMP还是HTTP?

    直播未来属于RTMP还是HTTP? HTTP 传视频比 RTMP 实现起来简单?HTTP 延迟太高? 答:直播通讯未来是属于html5的. 1,协议使用份额 如今国内90%的面向大众的直播平台都是采用 ...

  2. Win7_刻录DVD

    1.刻录 临时文件夹: 1.1.C:\Users\具体的用户名\AppData\Local\Microsoft\Windows\Burn 1.2.双击 插入刻录盘的光驱,直接将文件复制到 这里,实际上 ...

  3. JavaWeb学习总结(二)—http协议

    http协议概念: * 即超文本传输协议.它规定了浏览器与服务器之间的通讯规则. * http是基于请求/响应模式的,所以分为请求协议和响应协议 http的类型: HTTP协议的版本:HTTP/1.0 ...

  4. JavaSE复习_11 IO流复习

    △FileReader是使用默认码表读取文件, 如果需要使用指定码表读取, 那么可以使用InputStreamReader(字节流,编码表)    FileWriter是使用默认码表写出文件, 如果需 ...

  5. for循环与for in循环

    json是js里的一种数据格式.var obj={a:15,b:8,c:12} json数组对象 var arr=[15,8,12]; 数组alert(obj.a); ---15alert(obj[' ...

  6. iOS 开发之照片框架详解(1)

    http://kayosite.com/ios-development-and-detail-of-photo-framework.html/comment-page-1 一. 概要 在 iOS 设备 ...

  7. Android布局_网格布局GirdLayout

    自Android4.0版本后新增的GirdLayout网格布局(API 14) <?xml version="1.0" encoding="utf-8"? ...

  8. XML到底是什么

    http://www.w3school.com.cn/xml/xml_intro.asp w3school介绍 XML  XML 被设计用来传输和存储数据. HTML 被设计用来显示数据. 应该掌握的 ...

  9. Rocketmq-尝试理解

    普通的信息发送和消费 首先要启动nameserver和broker,nameserver是一个几乎无状态节点.broker分为master和slave,master和slave的对应关系通过指定相同的 ...

  10. wifi adb 调试手机

    首先手机,PC都连上WIFI, 如果可以用USB操作,在PC端,输入ping 手机的ip 地址,看看是否成功, 在PC端输入下面命令adb tcpip 5555adb connect 192.168. ...