简介

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. CDN学习笔记一(CDN是什么?)

    CDN是什么? 谈到CDN的作用,可以用8年买火车票的经历来形象比喻: 8年前,还没有火车票代售点一说,12306.cn更是无从说起.那时候火车票还只能在火车站的售票大厅购买,而我所住的小县城并不通火 ...

  2. 内部使用final参数的原因

    局部内部类(即:定义在方法中的内部类),访问方法中的局部变量 : 局部变量的生命周期与对象的生命周期的不一致性!方法在执行完方法的局部变量就消失,而内部类如果有引用还是存在的,那么将找不到变量.此时设 ...

  3. iOS JS交互

    1. 添加本地js文件, 并配置head中的属性 function increaseMaxZoomFactor() {        var element = document.createElem ...

  4. 卷积FFT、NTT、FWT

    先简短几句话说说FFT.... 多项式可用系数和点值表示,n个点可确定一个次数小于n的多项式. 多项式乘积为 f(x)*g(x),显然若已知f(x), g(x)的点值,O(n)可求得多项式乘积的点值. ...

  5. 构建maven项目3

    1.1.创建Jave Project 1.使用mvn archetype:generate命令,如下所示: mvn archetype:generate -DgroupId=com.mycompany ...

  6. mysql ERROR 1044 (42000): Access denied for user ''@'localhost' to database

    新安装的mysql密码是空的. ./mysql -u root -p use mysql SELECT `Host`,`User` FROM user; UPDATE user SET `Host` ...

  7. java.lang.UnsupportedClassVersionError: org/sonatype/nexus/bootstrap/jsw/JswLauncher : Unsupported major.minor version 51.0

    jdk 版本不对,需要修改jdk的版本

  8. TCP拥塞控制

    TCP必须使用端到端拥塞控制而不是使网络辅助的拥塞控制,因为IP层不向端系统提供显式的网络拥塞反馈.TCP采用的方法是让每一个发送方根据所感知到的网络拥塞程度来限制其能向连接发送流量的速率. 几个相关 ...

  9. tomcat集群配置

    http://www.finereporthelp.com/help/24/1/3/3.html#a:4.1

  10. Hive中JOIN操作

    1. 只支持相等JOIN. 2. 多表连接当使用不同的列进行JOIN时,会产生多个MR作业. 3. 最后的表的数据是从流中读取,而前面的会在内存中缓存,因此最好把最大的表放在最后. SELECT /* ...