Redis基础数据结构

基础数据结构

sds简单动态字符串

数据结构

typedef struct sdstr{
int len // 字符串分配的字节
int free // 未使用的字节数
char buff[] // 存储字符串的数组
}

sds是字符串对象的底层实现之一

sds的特性

赋值操作会统计字符串的长度然后将字符串存入buff里面,同时设定长度和使用的长度

例如 "hello"这个字符串的存储结构如下

{
len:5,
free:0,
buff:['h','e','l','l','o','\0']
}

修改的时候会比较麻烦,分为两种情况

一是由段字符串变长:例如:由"hello"变为"hello,redis".

这个时候系统会检查原本的sds字符串是否有空余空间,剩余空间为0,会分配等同于修改后字符串长度的剩余空间给sds,这个时候字符串的free属性会变为11,然后执行sdscat(),这个时候buff会变为['h','e','l','l','o',',','r','e','d','i','s','\0'],然后将字符串长度len修改为11

最终结构如下

{
len:11,
free:11,
buff:['h','e','l','l','o',',','r','e','d','i','s','\0']
}

ps:当长度小于1M是翻倍扩容,超过1M时是以1M为标准定量扩容

二是由长字符串变短

例如:由"hello,redis"变为"redis",这个时候会释放多余空间,同时把free值设为多出来的空间,以便下次使用方便

修改后的结构大概如下

{
len:5, // 字符串长度
free:17, // 原本11,加上释放到的6个字节
buff:['r','e','d','i','s','\0']
}

需要释放的时候可以手动调用函数来释放空间

为什么要使用sds?

  1. sds可以杜绝缓冲区溢出的问题,获取字符串长度复杂度为常数
  2. 二进制安全,sds使用len属性来判断字符串的结束
  3. 减少字符串修改时的内存重分配次数

链表

数据结构

//链表节点
typedef struct listNode{
struct listNode *pre;
struct listNode *next;
void *value;
}listNode; //链表
typedef struct list{
listNode * head; //头节点
listNode * tail; //尾节点
unsigned long len; //节点数量
void *(*dup)(void *ptr); //节点值复制函数
void (*free)(void *ptr); //节点值释放函数
void (*match)(void *ptr,void *key); //节点值对比函数
}

链表是列表对象的底层实现之一

链表在redis中主要负责的是存储和维护某一类对象,所常用到的操主要有遍历,修改等

链表在redis中使用极为广泛,redis的事务,发布与订阅,服务器中维护的redisClient信息等都是用链表结构进行的存储

字典

数据结构

//哈希表
typedef struct dictht{
dictEntry **table; //哈希节点
unsigned long size; //哈希表大小
unsigned long sizemask; //哈希表掩码,用于计算索引值
unsigned long used; // 已有节点数量
} dictht; //哈希节点
typedef struct dictEntry{
void *key;//键
union {
void *val;
uint64_tu64;
int64_ts64;
} v;
struct dictEntry *next;
}dictEntry; //字典
typedef struct dict{
dictType *type; //类型特定函数
void *privdata; //私有数据
dictht ht[2]; //哈希表 ht[0]常用 ht[1]rehash时候使用
int rehashidx; //rehash标识
}dict;

字典是数据库的底层实现

解决键冲突

redis使用链地址法(separate chaining)来解决键冲突,当两个键的index值相同时,会把第二个键放到第一个键的前面,查询时对这个index的哈希节点链表进行遍历

rehash:

当哈希表的负载因子(load factor)大于设定值时(平时为1,在BGREWRITEAOF时为5),哈希表会进行rehash操作

rehash采用渐进式的方式进行执行,具体流程就是把ht[0]里面的数据重新进行哈希计算放到ht[1],此时的哈希查询操作两个表同时提供服务,写入操作则只有ht[1]提供,这样ht[0]处于只减不增的状态,最终当ht[0]里面的所有数据都被转移到ht[1]时,rehashidx被设为-1,表明rehash结束,删除ht[0],并将ht[1]设为ht[0],同时重新分配新的ht[1]

ps:负载因子 = used /size;

跳跃表

数据结构

//跳跃表节点
typedef struct zskiplistNode {
sds ele;
double score;
struct zskiplistNode *backward;
struct zskiplistLevel {
struct zskiplistNode *forward;
unsigned int span;
} level[];
} zskiplistNode; //跳跃表
typedef struct zskiplist {
struct zskiplistNode *header, *tail;
unsigned long length;
int level;
} zskiplist;

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

跳跃表中的头结点不计算在length长度之内,跳跃表的节点排序按照分值从小到大排序

每次创建新节点的时候,redis会根据幂次定律随机生成一个1-32的层数作为level数组的大小,每个节点都有指向表尾方向的前进指针和之前表头方向的后退指针,这两个指针可以让程序方便的遍历所有节点,层的跨度用于记录两点之间的距离,跨度可以用来计算rank值.节点的分值是一个double值,节点的对象是一个指针,指向一个保存着sds字符串的字符串对象(下一节讲redis对象)

整数集合

数据结构

typedef struct intset{
uint32_t encoding;//编码方式
uint32_t length;//集合包含的元素数量
int8_t contents[];//保存元素的数组
} intset;

顾名思义整数集合是用来保存整数值的抽象数据结构

集合中不会出现重复元素

contents数组中保存的整数值有小到大排列

length等于contents的长度

虽然contents的定义是int8_t 但实际上contents的值类型由encoding决定

升级

当一个新元素超过原来整数集合encoding定义的值的类型时,会进行升级,升级结果会使集合的encoding变成所有数组中元素的值最大的数据类型,并且不支持降级

例如:有一个整数集合[1,2,3],本身的编码为int8,现在增加一个300的数字进该集合,会导致集合的编码升级为int16,这个时候列表的大小由8x3=24 变为 16x4=64,即便int8可以存储前三个值,但是为了简单起见,仍然会为集合中每一个元素分配同样的空间

压缩列表

压缩列表被用作列表键和哈希键的底层实现

压缩列表属于特殊的结构,是一种数据存储的方式,目的是为了节约内存,是一种采用特殊编码的连续内存块组成的顺序型(sequential)数据结构.

大致结构如下:

zlbytes zltail zllen entry1 entry2 ... zlend

每个压缩列表由如下三部分组成

previous_entry_length encoding content
前一节点的长度 记录content的类型和长度 节点的值

如果前一个节点长度小于254字节,previous_entry_length会使用1字节空间保存这个长度,如果大于254字节,将使用5字节长度保存这个值,这个机制会引起"连锁更新"

连锁更新: 假设现有连续的三个压缩列表节点l1,l2,l3,长度分别为 253,253,253,现在往第一个节点前添加一个长度超过254的节点,这个时候l1要给previous_entry_length分配5个字节来存储长度,所以列表本身长度会变为257,这将导致l2也需要5字节存储l1的长度,l3也会产生同样的变化,这样由一个列表操作引起的一系列更新操作成为连锁更新

Redis中的基本数据结构的更多相关文章

  1. Redis 中 5 种数据结构的使用场景介绍

    这篇文章主要介绍了Redis中5种数据结构的使用场景介绍,本文对Redis中的5种数据类型String.Hash.List.Set.Sorted Set做了讲解,需要的朋友可以参考下 一.redis ...

  2. Redis中5种数据结构的使用场景介绍

    转载于:http://www.itxuexiwang.com/a/shujukujishu/redis/2016/0216/108.html?1455861435 一.redis 数据结构使用场景 原 ...

  3. redis中5种数据结构的使用

    一.redis 数据结构使用场景 原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的源码.目前目标是吃透 redis 的数据结构.我们都知道,在 ...

  4. Redis中5种数据结构的使用场景

    一.redis 数据结构使用场景 原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的源码.目前目标是吃透 redis 的数据结构.我们都知道,在 ...

  5. Redis学习笔记之Redis中5种数据结构的使用场景介绍

    原来看过 redisbook 这本书,对 redis 的基本功能都已经熟悉了,从上周开始看 redis 的源码.目前目标是吃透 redis 的数据结构.我们都知道,在 redis 中一共有5种数据结构 ...

  6. Redis 中的数据库

    前面我们花了很多的时间介绍了 redis 中基本的数据结构,及其内部的实现情况,这些都是非常基础的东西,可能不经意间你就会用到他们,希望你花点时间了解一下. 接下来,我们将走近 redis 数据库,学 ...

  7. Redis解读(4):Redis中HyperLongLog、布隆过滤器、限流、Geo、及Scan等进阶应用

    Redis中的HyperLogLog 一般我们评估一个网站的访问量,有几个主要的参数: pv,Page View,网页的浏览量 uv,User View,访问的用户 一般来说,pv 或者 uv 的统计 ...

  8. Redis 中 String 类型的内存开销比较大

    使用 String 类型内存开销大 1.简单动态字符串 2.RedisObject 3.全局哈希表 使用 Hash 来存储 总结 参考 使用 String 类型内存开销大 如果我们有大量的数据需要来保 ...

  9. Redis中的数据结构与常用命令

    开发系统:Ubuntu 17.04Redis驱动:StackExchange.Redis 1.2.3Redis版本:3.2.1开发平台:.NET Core 对于Redis的介绍这里只写一句:Redis ...

随机推荐

  1. Android自定义评分控件:RatingStarView

    RatingStarView Android自定义的评分控件,类似ProgressBar那样的,使用星星图标(full.half.empty)作为progress标识的评分/打分控件. 效果图 图1: ...

  2. poj 1008

    #include<iostream>#include<string> using namespace std;string hname[19] = { "pop&qu ...

  3. 学习spring前,先了解了解代理模式

    什么是代理模式 举个例子,我是一个包租公,我现在想卖房,但是我不想麻烦,每天被电话骚扰,所以这个时候我找了楼下一个中介,让他帮我代理这些事,那么他自然有租房的方法.以后如果有人想租房,直接找中介就行了 ...

  4. Hibernate参数绑定的五种方式

    Hibernate参数绑定 参数绑定优点: (1)安全性 防止用户恶意输入条件和恶意调用存储过程 (2)提高性能 底层采用JDBC的PreparedStatement预定义sql功能,后期查询直接从缓 ...

  5. VFP 实验参考答案

    1.    使用for循环计算1到1000之间能既能被7整除也能被11整除的所有的数的和.(结果为6006)源程序代码 sum=0 i=1 for i=1 to 1000 if((i%7=0) and ...

  6. .Net程序员学用Oracle系列(7):视图、函数、存储过程、包

    1.视图 1.1.创建.删除及调用普通视图 1.2.高级视图介绍 2.函数 2.1.系统函数介绍 2.2.创建.删除及调用自定义函数 3.存储过程 3.1.创建.修改及删除存储过程 3.2.调用存储过 ...

  7. redis入门指南-附录B

  8. linux命令11

    tar命令的使用 tar文件是把几个文件的(或)目录集合在一个文件夹里,是创建备份和归档的最佳工具. [root@localhost ~]# tar --help用法: tar [选项...] [FI ...

  9. js实现导航菜单栏随着屏幕的滚动进行滚动的效果

    $(window).scroll(function () { var $nav = $(".floatingMenu ul li"), length = $nav.length-1 ...

  10. 自己动手实现html去标签和文本提取

    随意观看 [TOC] 工具 python3.6 正则表达式(别的语言思路一样,容易借鉴) python正则表达式:flags的应用 这里主要介绍一下re.compile(pattern[, flags ...