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. Bootstrap Flat UI的select下拉框显示不出来 问题解决

    Bootstrap Flat UI的select下拉框显示不出来?看这里,恰巧今天我也遇到了这个问题: 点击Messages后并没有出现下拉列表,然而官网的index.html却能显示出来. 经过一番 ...

  2. poj2185 Milking Grid

    题目链接:http://poj.org/problem?id=2185 这道题我看了好久,最后是通过参考kuangbin的博客才写出来的 感觉next数组的应用自己还是掌握的不够深入 这道题其实就是先 ...

  3. centos6.5软件安装:RPM,SRPM与yum功能

    鸟哥的linxu私房菜读书笔记 前言: Linux上软件的安装可以以原始码的方式来安装软件,也就是利用厂商释出的 Tarball 来进行软件的安装.不过,你应该很容易发现,那就是每次安装软件都需要侦测 ...

  4. Listener与Filter

    一.监听器Listener javaEE的13们规范中 包括servlet技术和jsp技术 servlet规范中包括三门技术:(servlet的三大组件) servelt技术  Listener技术 ...

  5. Gitlab使用Webhook实现Push代码自动部署

    1.Jenkins 安装完成以后,首先我们在Jenkins中需要安装一下,Gitlab Hook Plugin 插件: 2.插件安装完成我们创建任务,在任务重构建触发器下获取回调URL: 注意: 注意 ...

  6. 4.Java 加解密技术系列之 HMAC

    Java 加解密技术系列之 HMAC 序 背景 正文 代码 结束语 序 上一篇文章中简单的介绍了第二种单向加密算法 — —SHA,同时也给出了 SHA-1 的 Java 代码.有这方面需求的童鞋可以去 ...

  7. SharePoint 服务器端对象迁移文件夹

    最近有个需求,写个定时迁移文件夹的小程序,因为计时器任务比较繁琐,所以选择了控制台程序.然后,用windows的计划任务执行,也许,会有广大朋友需要类似的功能,简单的分享一下代码逻辑,功能非常简单,希 ...

  8. Java类加载器详解

    title: Java类加载器详解date: 2015-10-20 18:16:52tags: JVM--- ## JVM三种类型的类加载器- 我们首先看一下JVM预定义的三种类型类加载器,当一个 J ...

  9. 深入理解Java常用类-----StringBuilder

    上篇文章我们介绍过String这个常用类,知道了该类的内部其实是用的一个char数组表示一个字符串对象的,只是该字符数组被final修饰,一旦初始化就不能修改,但是对于经常做字符串修改操作的情况下,S ...

  10. Java 8——Optional

    本文主要介绍Java 8的 Optional 的简单使用 Address 1 2 3 4 5 6 7 @Data @AllArgsConstructor @NoArgsConstructor publ ...