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. SectionIndexer 利用系统的控件,制作比较美观的侧栏索引控件

    如上图所示,右侧的索引是系统提供的,具体使用方法,请搜索: SectionIndexer 相关的资料进行开发.

  2. day_1 练习2

    #!/usr/bin/env python# -*- coding:utf-8 -*-'''b. 功能要求: v = 2000 要求用户输入总资产,例如:2000 显示商品列表,让用户根据序号选择商品 ...

  3. Lazyman功能实现

    题目要求是这样的: 实现一个LazyMan,可以按照以下方式调用: LazyMan("Hank")输出: Hi! This is Hank! LazyMan("Hank& ...

  4. html页面顶部出现一段空白,检查控制台发现body 下出现&#65279字符,原因及解决办法

    html页面顶部出现一段空白,检查控制台发现body 下出现&#65279字符,原因及解决办法 分析: 原来是页面编码时增加了BOM,此页面后端数据主要是PHP语言,对PHP来讲PHP在设计时 ...

  5. SQL Sever2008 新手入门第一天安装软件

    (计应154兰家才)这学期,新来了一门课程,数据库应用.刚开始什么都不懂,也不知道这东西到底是干嘛,本着路漫漫其修远兮,吾将上下而求索的精神,开始了一段求知路程.刚开始找了一个简单的绿化版sql200 ...

  6. stl中的容器、迭代器和算法----vector中的find实现

    来源 http://blog.csdn.net/huangyimin/article/details/6133650 stl包括容器.迭代器和算法: 容器 用于管理一些相关的数据类型.每种容器都有它的 ...

  7. Ionic集成ArcGIS JavaScript API.md

    1. Ionic同原生ArcGIS JavaScript API结合 1.1. 安装esri-loader 在工程目录下命令行安装: npm install angular2-esri-loader ...

  8. 那些年,让我们一起着迷的Spring

    构建企业级应用框架(SpringMVC+Spring+Hibernate/ibatis[Mybatis]) 框架特点:半成品,封装了特定的处理流程和控制逻辑,成熟的,不断升级的软件.重用度高,开发效率 ...

  9. Qt Opencv 在Linux下摄像头简单示例(转)

    下面写的文章也许网上也有类似的,但是大多数都没有给出思路及背景,让初学者每次都只能学到一点皮毛,不少知识需要大量搜索零碎地拼凑起来.题外话,虽然现在是碎片化信息时代,但正是这样信息整合能力也显得非常重 ...

  10. javaWeb学习总结(8)- JSP标签(6)

    一.JSP标签介绍 JSP标签也称之为Jsp Action(JSP动作)元素,它用于在Jsp页面中提供业务逻辑功能,避免在JSP页面中直接编写java代码,造成jsp页面难以维护. 二.JSP常用标签 ...