第一篇文章,思来想去,写一写Redis吧,最近在深入研究它。

一丶Redis底层结构

1. redis 存储结构

  • redis的存储结构从外层往内层依次是redisDb、dict、dictht、dictEntry。
  • redis的Db默认情况下有16个,每个redisDb内部包含一个dict的数据结构。
  • redis的dict内部包含dictht的数组,数组个数为2,主要用于hash扩容使用。
  • dictht内部包含dictEntry的数组,可以理解就是hash的桶,然后如果冲突通过挂链法解决

redisServer内部包含着 redisDb *db的数组元素,只是用指针体现而已。

redisDb内部包含着dict *dict和dict *expires,用于存储数据和过期事件。

dict内部包含 dictht ht[2],是存储数据的对象,之所以有两个元素是为了扩容方便。

真正保存数据的核心数据结构, dictEntry **table可以理解为hash的桶,通过挂链法解决冲突。

存储数据的单个节点,包含key和value。保存我们存储在redis的数据。

2.redis 数据存储过程

数据存储过程以set为例作为说明,过程如下:

  • 从redisDb当中找到dict,每个db就一个dict而已。
  • 从dict当中选择具体的dictht对象。
  • 首先根据key计算hash桶的位置,也就是index。
  • 新建一个DictEntry对象用于保存key/value,将新增的entry挂到dictht的table对应的hash桶当中,每次保存到挂链的头部。
  • dictSetKey的宏保存key
  • dictSetVal的宏保存value

3.redis 过期事件存储过程

redis的过期事件存储在db->expires的对象当中,整个设置过期时间的过程如下:

  • 从db-dict获取原来存储数据,之所以去取数是为了保证key的存在性
  • 从db->expires获取旧的过期事件并重新计算过期时间dictReplaceRaw
  • 过期时间重新保存到DictEntry当中,也就是db->expires中的某个对象。

4.命令执行过程

整个redis的server端命令执行过程就如下面这个流程图:

  1. nio层读取数据
  2. 解析数据到命令行格式
  3. 查找命令对应的执行函数执行命令
  4. 同步数据到slave和aof

二丶Redis底层数据结构

1.string

  • 整数:存储字符串长度小于21且能够转化为整数的字符串。
  • emstr:存储字符串长度小于39的字符串
  • sds:剩余情况使用sds进行存储。

embstr和sds的区别在于内存的申请和回收

embstr的创建只需分配一次内存,而raw为两次(一次为sds分配对象,另一次为redisObject分配对象,embstr省去了第一次)。相对地,释放内存的次数也由两次变为一次。

embstr的redisObject和sds放在一起,更好地利用缓存带来的优势

缺点:redis并未提供任何修改embstr的方式,即embstr是只读的形式。对embstr的修改实际上是先转换为raw再进行修改。

用SDS保存字符串 “Redis”具体图示如下:

2.List

redis list数据结构底层采用压缩列表ziplist或linkedlist两种数据结构进行存储,首先以ziplist进行存储,在不满足ziplist的存储要求后转换为linkedlist列表。

当列表对象同时满足以下两个条件时,列表对象使用ziplist进行存储,否则用linkedlist存储。

  • 列表对象保存的所有字符串元素的长度小于64字节
  • 列表对象保存的元素数量小于512个。

redis list元素添加过程:

list的数据添加根据传入的变量个数一个个顺序添加,整个顺序如下:

  • 创建list对象并添加到db的数据结构当中
  • 针对每个待插入的元素添加到list当中
  • list的每个元素的插入过程中,我们会对是否需要进行转码作两个判断:
  • 对每个插入元素的长度进行判断是否进行ziplist->linkedlist的转码。
  • 对list总长度是否超过ziplist最大长度的判断。

3.hashmap

redis的哈希对象的底层存储可以使用ziplist(压缩列表)和hashtable。当hash对象可以同时满足一下两个条件时,哈希对象使用ziplist编码。

  • 哈希对象保存的所有键值对的键和值的字符串长度都小于64字节
  • 哈希对象保存的键值对数量小于512个

4.set

redis的集合对象set的底层存储结构特别神奇,我估计一般人想象不到,底层使用了intset和hashtable两种数据结构存储的,intset我们可以理解为数组,hashtable就是普通的哈希表(key为set的值,value为null)。是不是觉得用hashtable存储set是一件很神奇的事情。

set的底层存储intset和hashtable是存在编码转换的,使用intset存储必须满足下面两个条件,否则使用hashtable,条件如下:

  • 结合对象保存的所有元素都是整数值
  • 集合对象保存的元素数量不超过512个

5.zset

zset底层的存储结构包括ziplist或skiplist,在同时满足以下两个条件的时候使用ziplist,其他时候使用skiplist,两个条件如下:

  • 有序集合保存的元素数量小于128个
  • 有序集合保存的所有元素的长度小于64字节

当ziplist作为zset的底层存储结构时候,每个集合元素使用两个紧挨在一起的压缩列表节点来保存,第一个节点保存元素的成员,第二个元素保存元素的分值。

当skiplist作为zset的底层存储结构的时候,使用skiplist按序保存元素及分值,使用dict来保存元素和分值的映射关系。

6.数据结构

ziplist:

压缩列表,压缩列表(ziplist)是Redis为了节省内存而开发的,是由一系列特殊编码的连续内存块组成的顺序型数据结构,一个压缩列表可以包含任意多个节点(entry),每个节点可以保存一个字节数组或者一个整数值。

压缩列表的原理:压缩列表并不是对数据利用某种算法进行压缩,而是将数据按照一定规则编码在一块连续的内存区域,目的是节省内存。

ziplist的数据结构主要包括两层,ziplist和zipEntry。

  • ziplist包括zip header、zip entry、zip end三个模块。
  • zip entry由prevlen、encoding&length、value三部分组成。
  • prevlen主要是指前面zipEntry的长度,coding&length是指编码字段长度和实际- 存储value的长度,value是指真正的内容。
  • 每个key/value存储结果中key用一个zipEntry存储,value用一个zipEntry存储。

 inset:

intset内部其实是一个数组(int8_t coentents[]数组),而且存储数据的时候是有序的,因为在查找数据的时候是通过二分查找来实现的。整数集合(intset)是Redis用于保存整数值的集合抽象数据类型,它可以保存类型为int16_t、int32_t 或者int64_t 的整数值,并且保证集合中不会出现重复元素。

整数集合的每个元素都是 contents 数组的一个数据项,它们

升级

当我们新增的元素类型比原集合元素类型的长度要大时,需要对整数集合进行升级,才能将新元素放入整数集合中。具体步骤:

  1. 根据新元素类型,扩展整数集合底层数组的大小,并为新元素分配空间。
  2. 将底层数组现有的所有元素都转成与新元素相同类型的元素,并将转换后的元素放到正确的位置,放置过程中,维持整个元素顺序都是有序的。
  3. 将新元素添加到整数集合中(保证有序)。

升级能极大地节省内存。

降级

整数集合不支持降级操作,一旦对数组进行了升级,编码就会一直保持升级后的状态。

按照从小到大的顺序排列,并且不包含任何重复项。

length 属性记录了 contents 数组的大小。

需要注意的是虽然 contents 数组声明为 int8_t 类型,但是实际上contents 数组并不保存任何 int8_t 类型的值,其真正类型有 encoding 来决定。

skiplist:

跳跃表(skiplist)是一种有序数据结构,它通过在每个节点中维持多个指向其它节点的指针,从而达到快速访问节点的目的。具有如下性质:

  1. 由很多层结构组成;
  2. 每一层都是一个有序的链表,排列顺序为由高层到底层,都至少包含两个链表节点,分别是前面的head节点和后面的nil节点;
  3. 最底层的链表包含了所有的元素;
  4. 如果一个元素出现在某一层的链表中,那么在该层之下的链表也全都会出现(上一层的元素是当前层的元素的子集);
  5. 链表中的每个节点都包含两个指针,一个指向同一层的下一个链表节点,另一个指向下一层的同一个链表节点;

Redis底层结构全了解的更多相关文章

  1. Redis底层结构概述

    可以使用 object encoding <key> 查看使用的具体数据结构 原图链接

  2. Redis短结构

    本章将介绍3种非常有价值的降低Redis内存占用的方法.降低Redis的内存占用有助于减少创建快照和加载快照所需的时间.提升载入AOF文件和重写AOF文件时的效率.缩短从服务器进行同步所需的时间,并且 ...

  3. Redis的结构和运作机制

    目录 1.数据库的结构 1.1 字典的底层实现 2.过期键的检查和清除 2.1 定时删除 2.2 惰性删除 2.3 定期删除 2.4 对RDB.AOF和复制的影响 3.持久化机制 3.1 RDB方式 ...

  4. Redis短结构与分片

    本文将介绍两种降低Redis内存占用的方法——使用短结构存储数据和对数据进行分片. 降低Redis内存占用有助于减少创建快照和加载快照所需的时间.提升载入AOF文件和重写AOF文件时的效率.缩短从服务 ...

  5. redis底层设计(三)——redis数据类型

    今天我们来看一下redis的数据类型.既然redis的键值对可以保存不同类型的值,那么很自然就需要对键值对的类型进行检查以及多态处理.下面我们将对redis所使用的对象系统进行了解,并分别观察字符串. ...

  6. redis底层设计(一)——内部数据结构

    redis是一个key-value存储系统.和Memcached类似,它支持存储的value类型相对更多,包括string(字符串).list(链表).set(集合).zset(sorted set ...

  7. 对于redis底层框架的理解(一)

    近期学习了redis底层框架,好多东西之前都没听说过,算是大开眼界了. 先梳理下redis正常的通讯流程吧 首先服务器启动都有主函数main,这个main函数就在redis.c里 首先是initser ...

  8. 恋恋风辰 对于redis底层框架的理解(一)

    近期学习了redis底层框架,好多东西之前都没听说过,算是大开眼界了. 先梳理下redis正常的通讯流程吧 首先服务器启动都有主函数main,这个main函数就在redis.c里 首先是initser ...

  9. Redis 底层数据结构介绍

    Redis 底层数据结构 版本:2.9 支持的数据类型: 字符串 散列 列表 集合 有序集合 字符串 Redis 利用原生的 c 字符串进行了一次封装.封装的字符串叫做简单动态字符串:SDS(simp ...

随机推荐

  1. poj2125最小点权覆盖+找一个割集

    Destroying The Graph Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 8503   Accepted: 2 ...

  2. Istio的流量管理(实操二)(istio 系列四)

    Istio的流量管理(实操二)(istio 系列四) 涵盖官方文档Traffic Management章节中的inrgess部分. 目录 Istio的流量管理(实操二)(istio 系列四) Ingr ...

  3. java后端解决跨域

    1 package com.zys.boot_jeep.config; import org.springframework.context.annotation.Bean; import org.s ...

  4. SQL面试50题

    1.查询课程编号为“01”的课程比“02”的课程成绩高的所有学生的学号(重点) SELECT a.s_id,a.s_score FROM (') as a INNER JOIN (') as b on ...

  5. Jquery toggle

    toggle:切换显示 如 <script> $(document).ready(function(){ $("button").click(function(){ $ ...

  6. [推荐]大量 Blazor 学习资源(二)

    继上一篇<[推荐]大量 Blazor 学习资源(一)>之后,社区反应不错,但因个人原因导致这篇文章姗姗来迟,不过最终还是来了!这篇文章主要收集一些常用组件.书籍和电子书. 资料来源:htt ...

  7. 什么,容器太多操作不过来?我选择Docker Compose梭哈

    接上一篇:面试官:你说你精通 Docker,那你来详细说说 Dockerfile 吧 一.容器之间通信 1.单向通信 1.1.什么意思 mysql和tomcat是两个独立的容器,但是tomcat需要和 ...

  8. 数据库-第八章 数据库编程-8.4 ODBC编程

    ODBC编程 一.ODBC概述 二.ODBC工作原理概述 1.用户应用程序 2.ODBC驱动程序管理器 3.数据库驱动程序 4.数据源管理 5.小结 三.ODBC API基础 1.函数概述 2.句柄及 ...

  9. Nuxt.js

    nuxt.js简单来说是Vue.js的通用框架,最常用的就是SSR(服务端渲染),nuxt.js这个框架,用Vue开发多页面应用,并在服务端完成渲染,可以直接用命令把我们制作的vue项目生成为静态的h ...

  10. 最短路径——Dijkstar算法

    背景:本文是在小甲鱼数据结构教学视频中的代码的基础上,添加详细注释而完成的.该段代码并不完整,仅摘录了核心算法部分,结合自己的思考,谈谈理解. Dijkstar算法理解: Dijkstar算法的核心思 ...