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. OC中的私有变量和私有方法

    在类的实现即.m文件中也可以声明成员变量,但是因为在其他文件中通常都只是包含头文件而不会包含实现文件,所以在.m文件中声明的成员变量是@private得.在 .m中定义的成员变量不能和它的头文件.h中 ...

  2. silverlight WPF 水纹文本框

    silverlight3取消了watertextbox控件,只有自己实现了个,实现了和textbox一样的无差异使用,只需要设置defaulttext就可以了 using System; using ...

  3. jQuery生成元素(table)并绑定样式和事件

    L略有重复

  4. AutoFac学习摘要

    依赖注入(控制反转)常见的依赖注入工具:AutoFac,Spring.Net,Unity等依赖注入的方式:1.通过构造函数进行注入2.通过属性进行注入 注意:在项目中AutoFac的注入有两次,第一次 ...

  5. 解决jmeter乱码问题(改配置文件)

    1.先改脚本里面的 content encoding为utf-8 然后response为utf-8 2.如果以上还是不可以,那就改配置文件jmeter.properties,里面的sampleresu ...

  6. 初识Eclipse!!

      Java之路 ——初识Eclipse   零.大纲 一.前言 二.获取Eclipse 三.运行Eclipse 四.创建及运行第一个Java Project 五.界面介绍 六.如何调试 七.获取插件 ...

  7. poj3159 Candies SPFA

    题目链接:http://poj.org/problem?id=3159 题目很容易理解 就是简单的SPFA算法应用 刚开始用STL里的队列超时了,自己写了个栈,果断过,看来有时候栈还是快啊.... 代 ...

  8. JAVA Semaphore详解

    Semaphore(信号量):是一种计数器,用来保护一个或者多个共享资源的访问.如果线程要访问一个资源就必须先获得信号量.如果信号量内部计数器大于0,信号量减1,然后允许共享这个资源:否则,如果信号量 ...

  9. 【JAVAWEB学习笔记】25_Linux基础

    Linux基础 学习目标 1.了解Linux的简介与安装 2.掌握Linux常用的命令 3.掌握Linux系统上JDK.Mysql.Tomcat的安装 一.Linux的简介 1.Linux的概述 Li ...

  10. Java垃圾回收--判断可触及性

    博客搬家自https://my.oschina.net/itsyizu/blog/ 垃圾回收的基本思想: 考察每一个对象的可触及性(从根节点开始是否可以访问到这个对象,如果可以,这说明当前对象正在使用 ...