redis底层是用什么结构来存储数据的呢?

我们从源码上去理解就会容易的多:

  redis底层是使用C语言来编写的,我们可以看到它的数据结构声明。一个 dict 有两个dictht,一个dictht有一个dictEntry数组,每个dictEntry有next指针,redisObject是真正存储redis各种类型的结构。因此是一个链表结构。从上面的分析可以看出Redis用拉链法解决冲突的哈希表结构。

“链地址法”的问题在于当碰撞剧烈时,性能退化严重,例如:当有n个数据,m个槽位,如果m=1,则整个Hash表退化为链表,查询复杂度O(n)

为了避免Hash碰撞,Redis的方案是“双dictht”,正常流程使用一个dictht,当发现碰撞剧烈(判断依据为当前槽位数和Key数的对比),分配一个更大的dictht,然后逐步将数据从老的dictht迁移到新的dictht上去。这就需要进行rehash

typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
} dict;
/* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
//redisObject是真正存储redis各种类型的结构,定义如下:
#define REDIS_STRING 0
#define REDIS_LIST 1
#define REDIS_SET 2
#define REDIS_ZSET 3
#define REDIS_HASH 4
typedef struct redisObject {
unsigned type:1; //逻辑类型
unsigned notused:2; /* Not used */
unsigned encoding:4; //物理存储类型
unsigned lru:22; /* lru time (relative to server.lruclock) */
int refcount;
void *ptr; //具体数据
} robj;

如下是rehash方法的源码

rehash 操作不是一次性完成,而是采用渐进方式,这是为了避免一次性执行过多的 rehash 操作给服务器带来过大的负担。

  • 渐进式 rehash 通过记录 dict 的 rehashidx 完成,它从0开始然后每执行一次rehash都会递增。例如在一次 rehash 中,要把 dict[0] rehash到dict[1],这一次会把 dict[0] 上 table[rehashidx] 的键值对 rehash 到 dict[1] 上,dict[0] 的 table[rehashidx] 指向 null,并令 rehashidx++。
  • 在 rehash 期间,每次对字典执行添加、删除、查找或者更新操作时,都会执行一次渐进式 rehash。
  • 采用渐进式 rehash 会导致字典中的数据分散在两个 dictht 上,因此对字典的操作也需要到对应的 dictht 去执行。
int dictRehash(dict *d, int n) {
int empty_visits = n * 10; /* Max number of empty buckets to visit. */
if (!dictIsRehashing(d)) return 0; while (n-- && d->ht[0].used != 0) {
dictEntry *de, *nextde; /* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
assert(d->ht[0].size > (unsigned long) d->rehashidx);
while (d->ht[0].table[d->rehashidx] == NULL) {
d->rehashidx++;
if (--empty_visits == 0) return 1;
}
de = d->ht[0].table[d->rehashidx];
/* Move all the keys in this bucket from the old to the new hash HT */
while (de) {
uint64_t h; nextde = de->next;
/* Get the index in the new hash table */
h = dictHashKey(d, de->key) & d->ht[1].sizemask;
de->next = d->ht[1].table[h];
d->ht[1].table[h] = de;
d->ht[0].used--;
d->ht[1].used++;
de = nextde;
}
d->ht[0].table[d->rehashidx] = NULL;
d->rehashidx++;
} /* Check if we already rehashed the whole table... */
if (d->ht[0].used == 0) {
zfree(d->ht[0].table);
d->ht[0] = d->ht[1];
_dictReset(&d->ht[1]);
d->rehashidx = -1;
return 0;
} /* More to rehash... */
return 1;
}

那底层数据的有序性是如何实现的呢?

跳跃表是有序集合的底层实现之一。

  • 跳跃表是基于多指针有序链表实现的,可以看成多个有序链表。
  • 跳跃表是一种随机化数据结构,查找、添加、删除操作都可以在对数期望时间下完成。
  • 跳跃表目前在 Redis 的唯一作用,就是作为有序集类型的底层数据结构(之一,另一个构成有序集的结构是字典)。
  • 与红黑树等平衡树相比,跳跃表具有以下优点:
    • 插入速度非常快速,因为不需要平衡树的旋转操作;
    • 更容易实现;
    • 支持无锁操作。

跳跃表的定义可以在任何一本算法或数据结构的书中找到, 在这不介绍跳跃表的具体实现方式或者具体的算法。推荐一篇漫画,可以快速理解跳跃表,想要深入理解跳跃表,推荐一篇博客

Redis数据的底层存储原理的更多相关文章

  1. HBase底层存储原理

    HBase底层存储原理——我靠,和cassandra本质上没有区别啊!都是kv 列存储,只是一个是p2p另一个是集中式而已! 首先HBase不同于一般的关系数据库, 它是一个适合于非结构化数据存储的数 ...

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

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

  3. Protobuf底层存储原理

    参考官网, 序列化原理 底层二进制存储 message Test1 { optional int32 a = 1; } 并设置为a=150,序列化到一个文件中,查看文件,得到下面的二进制: 08 96 ...

  4. HBase底层存储原理——我靠,和cassandra本质上没有区别啊!都是kv 列存储,只是一个是p2p另一个是集中式而已!

    理解HBase(一个开源的Google的BigTable实际应用)最大的困难是HBase的数据结构概念究竟是什么?首先HBase不同于一般的关系数据库,它是一个适合于非结构化数据存储的数据库.另一个不 ...

  5. python-变量&底层存储原理

    目录 1.变量 1.变量如何使用 2.变量存储的原理 --[ 重点 ] 3.变量存储要遵循印射关系 4.变量三要素 2.常量 3.底层优化 4.垃圾回收机制 1.变量 1.变量如何使用 1.什么是变量 ...

  6. 高性能的Redis之对象底层实现原理详解

    对象 在前面的数个章节里, 我们陆续介绍了 Redis 用到的所有主要数据结构, 比如简单动态字符串(SDS).双端链表.字典.压缩列表.整数集合, 等等. Redis 并没有直接使用这些数据结构来实 ...

  7. 列式数据库~clickhouse 底层存储原理

    简介:今天介绍列式数据库的一些基本原理 一  数据目录 Data目录 数据存储目录,数据按照part分成多个文件夹,每个文件夹下存储相应数据和对应的元信息文件 Metadata 表定义语句,存储所有表 ...

  8. Redis(一):基本数据类型与底层存储结构

    最近在整理有关redis的相关知识,对于redis的基本数据类型以及其底层的存储结构简要的进行汇总和备注(主要为面试用) Redis对外提供的基本数据类型主要为五类,分别是 STRING:可以存储字符 ...

  9. V7000存储数据恢复_底层结构原理拆解及Mdisk磁盘掉线数据恢复方法

    Storwize V7000(也就是我们常说的V7000)是新推出的一款中端存储系统,这款系统的定位虽然在中端,但是Storwize V7000提供有存储管理功能,这一功能以前只有高端存储才拥有(例如 ...

随机推荐

  1. IE的if条件判断

    我们经常需要编写兼容代码,特别是万恶的IE. 特此记录下IE提供的if判断: <!--[if IE 5]> 仅IE5.0与IE5.5可以识别 <![endif]--> < ...

  2. Java泛型的逆变

    在上篇<Java泛型的协变>这篇文章中遗留以下问题——协变不能解决将子类型添加到父类型的泛型列表中.本篇将用逆变来解决这个问题. 实验准备 我们首先增加以下方法,见代码清单1所示. 代码清 ...

  3. HTTP协议状态码学习

    一直以来都在追求实战,从而忽视了对理论知识的深入学习和理解.这并不可怕,可怕的是当意识到自己的不足时,没有行动. 1xx(临时响应) 表示临时响应并需要请求者继续执行操作的状态代码. 代码   说明 ...

  4. SQL 之连接查询

    概述:INNER JOIN.LEFT JOIN.LIGHT JOIN.FULL JOIN. 一.INNER JOIN INNER JOIN 关键字在表中存在至少一个匹配时返回行. 语法: select ...

  5. google Chrome打开多个网站时等待可用的套接字,怎么加大连接数量提升速度

    这是因为Chrome对同一个域名的sockets连接数作了限制,或者你访问的网站经常被墙的原因.打开Chrome的Sockets:chrome://net-internals/#sockets 点击里 ...

  6. Microsoft ReportViewer 控件类型版本兼容问题及解决方法

    错误 Parser Error Message: The base class includes the field 'rvEquipment', but its type (Microsoft.Re ...

  7. C#学习笔记-继承基本知识

    namespace Inheritance { class Program { static void Main(string[] args) { Dog dog = new Dog(); dog.A ...

  8. 纯CSS实现3D照片墙

    HTML部分: <body> <div class="photo-wrap"> <!-- 舞台 --> <div class=" ...

  9. JavaScript自定义字符串格式化

    在JS中没有字符串拼接的方法,如过要使用怎么办呢?这时我们可以通过字符串的prototype可以自定义方法. <script> String.prototype.format = func ...

  10. .net C# Sql数据库SQLHelper类

    using System;using System.Collections.Generic;using System.Text;using System.Collections;using Syste ...