zoukankan      html  css  js  c++  java
  • Redis数据结构原理

    可能就前面的记录一下,后面的很多是截图存在Typora上。。粘过来不显示,我也懒得弄了,应该是比较完整的笔记记录了,这里只放了一小部分,有了这些完全可以自己写一个小Redis玩具了,有空试试吧

    简单动态字符串SDS

    Redis没有直接使用C语言传统的字符串表示,而使自己构建了一种名为简单动态字符串的抽象SDS

    C 字符串SDS
    获取字符串长度的复杂度为 O(N) 。 获取字符串长度的复杂度为 O(1) 。
    API 是不安全的,可能会造成缓冲区溢出。 API 是安全的,不会造成缓冲区溢出。
    修改字符串长度 N 次必然需要执行 N 次内存重分配。 修改字符串长度 N 次最多需要执行 N 次内存重分配。
    只能保存文本数据。 可以保存文本或者二进制数据。
    可以使用所有 库中的函数。 | 可以使用一部分 库中的函数。  

    Redis 的链表实现的特性可以总结如下:

    • 双端: 链表节点带有 prevnext 指针, 获取某个节点的前置节点和后置节点的复杂度都是 O(1) 。

    • 无环: 表头节点的 prev 指针和表尾节点的 next 指针都指向 NULL , 对链表的访问以 NULL 为终点。

    • 带表头指针和表尾指针: 通过 list 结构的 head 指针和 tail 指针, 程序获取链表的表头节点和表尾节点的复杂度为 O(1) 。

    • 带链表长度计数器: 程序使用 list 结构的 len 属性来对 list 持有的链表节点进行计数, 程序获取链表中节点数量的复杂度为 O(1) 。

    • 多态: 链表节点使用 void* 指针来保存节点值, 并且可以通过 list 结构的 dupfreematch 三个属性为节点值设置类型特定函数, 所以链表可以用于保存各种不同类型的值。

    字典dict

    image-20200807080457537

    image-20200807081042619

    image-20200807081110869

    rehash

    一般字典都会有ht[0] ht[1]两个字典,而ht1就是rehash时用的

    步骤:

    1. 判断是收缩还是扩容,确定ht1的size

    2. 将ht0的数据迁移到ht1

    3. 释放ht0,ht1成为ht0,新建ht1

    当满足以下条件时,哈希表会进行扩展和收缩

    1. 服务器目前没有执行BGSIVE或者BGREWRITEAOF命令,并且哈希表的负载因子>=1

    2. 服务器目前正在执行这两条命令之一,且负载因子>=5

    3. 负载因子=ht[0].used / ht[0].size

    4. 负载因子小于0.1 进行收缩

    原因:处于对执行BGSIVE或者BGREWRITEAOF命令时期时,redis会创建服务器进程的子进程,一般采用写时复制的策略优化子进程效率,所以子进程存在时会尽量避免rehash,因此提高了此时所需的负载因子

    其次:rehash是渐进rehash

    也就是说redis实际执行rehash时是采用每次对字典增删改查时顺便rehash,一些操作过后,字典rehash完成。当然了,在此期间,比如对字典的查找是在ht0和ht1上都进行查找

    image-20200807082928603

    跳跃表

    跳跃表是一种有序数据结构,支持Ologn的查找,最坏On。还可以通过顺序性操作批量处理节点

    image-20200807083321840

    • header | tail 头尾指针

    • level 跳跃表层数最大的节点(排除表头节点)

    • 每层 记录了这一跳的前进指针以及跨度这两个属性,显然层数越多跳跃表的查找遍历就越快,跨度用来统计走过的距离,方便统计Rank

    • 后退制作backward BW标记了向前一个结点的倒退指针,以实现表尾到表头的遍历操作

    • 分值和对象,value从小到大构成跳跃表,对象保存一个SDS,value相同时按SDS字典序排

    遍历时从高层往下查,碰到的前进指针跨度是1是往下走,知道碰到null证明到头了

    image-20200807084349299

    整数集合

    整数集合是集合间的底层实现之一,当set只有整数元素并且数量不多时,会采用整数集合

    image-20200807084742022

    升级,但不支持降级

    每当新添加的整数类型比现有类型长时,会进行升级操作,这样的动态策略可以节省内存

    压缩列表

    列表键和哈希键的底层实现之一

    列表短且只有整数时使用压缩列表,zset的键值对数量小于128,长度都小于64字节时也使用这个

    AOF

    与rdb保存数据库键值对不同,AOF持久化通过保存Redis执行的写命令持久化

    AOF持久化的实现

    可以分为命令 追加append 文件写入 文件同步sync三个步骤

    命令追加

    比如SET KEY VALUE执行后,这条命令会最佳到服务器状态的aof_buf

    缓冲区的末尾

    AOF文件的写入与同步以及appendfsync选项

    Redis 服务器进程就是一个时间循环,这个循环中的文件事件负责接收客户端的命令请求,以及向客户端发送命令回复,而时间事件负责执行serverCron函数这样需要定时运行的函数,

    image-20200821143202503

    image-20200821143515043

    fsync和fdatasync同步函数

    现代操作系统中用户调用write函数,通常先把数据放到内存缓冲区中,等到缓冲区满了或到了指定时间才会写入,这样虽然提高效率但也有停机时的不安全性,使用这两个同步函数强制操作系统将缓冲区的数据写入磁盘

    appendfsync选项的值得效果

    always时,最安全但也最慢,他最多只丢失一个事件循环中的命令数据

    everysec时,每秒都有负责这个的子线程进行同步,够快且最多丢失一秒的追加命令

    no时,同样每个事件循环都把缓冲区数据写到aof,但aof文件的同步由操作系统决定,因为一般不需要同步,所以这种模式的aof文件写入速度很快,但一旦发生丢失数据则会丢失上次同步aof后的所有数据

    AOF重写

    利用命令的合并和覆盖,创建一个新的aof文件替代原有的aof,新的aof只执行持久化必须的一部分的命令体积小很多

    重写伪代码p150

    子进程AOF重写后的同步
  • 相关阅读:
    Btrace
    ThreadPoolExecutor线程池参数设置技巧
    工具篇-NotePad++/JSON格式化
    springcloud-- Alibaba-nacos--支持的几种服务消费方式
    @RequestParam和@RequestBody的区别
    Excel 2013如何判断单元格里是否包含某个字符
    redis 通配符批量删除key
    字节真题 ZJ26-异或:使用字典树代替暴力破解降低时间复杂度
    约瑟夫环问题解决方法时间复杂度分析
    九字真言
  • 原文地址:https://www.cnblogs.com/handso/p/14074008.html
Copyright © 2011-2022 走看看