Redis设计实现-学习笔记

发布时间:2022-06-21 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了Redis设计实现-学习笔记脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

最近在准备面试,问到redis相关知识,只能说个皮毛,说的既不深入也不全面,所以抓紧突击一下,先学《redis设计与实现》。

选择看书的原因是:

书中全面深入,且能出书一定十分用心;

搜博客也找不到比书更全面的文章,且费时;

直接看码一个是对C掌握不好,且易困,效率不高,所以跟着书同步学源码,是我认为现在最好的选择。

 


 一:五种常用数据类型

简单动态字符串

redis做了一个用作字符串的SDS,除了一些不需要修改的场景,都是用SDS

C字符串的底层实现总是一 个N+1个字符长的数组

sds.h:

struct sdshdr {
    
    // buf 中已占用空间的长度
    int len;

    // buf 中剩余可用空间的长度
    int free;

    // 数据空间
    char buf[];
};

 

C字符串与SDS区别
1 C字符串 SDS
2 求长度,需要遍历O(n) 直接取len就可以O(1)
3 容易造成缓冲区溢出  
4  

减少了内存重新分配次数

(速度要求严苛,避免分配内存耗时多)

5   二进制安全

4:SDS有free,可以比C字符串多一些预留空间。空间优化策略主要有两种:

  • 空间预分配
    1. SDS进行修改时,会额外获得未使用空间。
    2. 修改后空间n
      1. n<1M;free分配n+1byte(空字符)
      2. n>=1M;free分配1M+1byte
  • 惰性空间释放
    1. SDS进行缩短时,不释放删除的空间,加到free里。

SDS的API

  • sdsnew 创建
  • sdSEMpty 创建空的SDS
  • sdsfree 释放
  • sdslen 获取len
  • sdsvail 获取free数
  • sdsdup 创建一个sds副本
  • 。。

 

链表

 redis自己构建的链表:

/*
 * 双端链表节点
 */
tyPEdef struct listNode {

    // 前置节点
    struct listNode *prev;

    // 后置节点
    struct listNode *next;

    // 节点的值
    void *value;

} listNode;
/*
 * 双端链表迭代器
 */
typedef struct listITer {

    // 当前迭代到的节点
    listNode *next;

    // 迭代的方向
    int direction;

} listIter;
/*
 * 双端链表结构
 */
typedef struct list {

    // 表头节点
    listNode *head;

    // 表尾节点
    listNode *tail;

    // 节点值复制函数
    void *(*dup)(void *ptr);

    // 节点值释放函数
    void (*free)(void *ptr);

    // 节点值对比函数
    int (*match)(void *ptr, void *key);

    // 链表所包含的节点数量
    unsigned long len;

} list;

字典

也叫:符号表、关联数组、映射,用于保存键值对;

/*
 * 字典
 */
typedef struct dict {

    // 类型特定函数
    dictType *type;

    // 私有数据
    void *PRivdata;

    // 哈希表
    dictht ht[2];

    // rehash 索引
    // 当 rehash 不在进行时,值为 -1
    int rehashidx; /* rehashing not in progress if rehashidx == -1 */

    // 目前正在运行的安全迭代器的数量
    int iterators; /* number of iterators currently running */

} dict;

 

Redis设计实现-学习笔记

 理解:

  • 字典dict;
    • 属性type是指向dictType的;dictType是保存特定类型键值对的函数;redis会为不同的字典设置不同的函数。
    • 属性privateData是存储对应类型的可选参数。
    • ht是指向dictht的引用;
      • 其中table是指向dictEntry的二维引用,有两级。
      • 第一级是hash后的值,到阈值就要rehash扩缩容
        • dictEntry是哈希节点
        • key是键
        • value是值
        • 还有指向下一个节点的引用next,用于成链。

 

hash算法

冲突解决

哈希表使用链地址法来解决键冲突:

哈希表节点dictEntry的指针构成一个链,hash相同的就排在当前dictEntry的next

rehash

1.为ht[1]分配空间,与ht[0]比较,扩容则分配

 // 新哈希表的大小至少是目前已使用节点数的两倍
 // T = O(N)
return dictExpand(d, d->ht[0].used*2);

收缩则分配大小等于ht[0]的?

/*
 * 缩小给定字典
 * 让它的已用节点数和字典大小之间的比率接近 1:1
 * 返回 DICT_ERR 表示字典已经在 rehash ,或者 dict_can_resize 为假。
 * 成功创建体积更小的 ht[1] ,可以开始 resize 时,返回 DICT_OK。
 */
int dictResize(dict *d)
{
    int minimal;
    // 不能在关闭 rehash 或者正在 rehash 的时候调用
    if (!dict_can_resize || dictIsRehashing(d)) return DICT_ERR;
    // 计算让比率接近 1:1 所需要的最少节点数量
    minimal = d->ht[0].used;
    if (minimal < DICT_HT_INITIAL_SIZE)
        minimal = DICT_HT_INITIAL_SIZE;
    // 调整字典的大小
    // T = O(N)
    return dictExpand(d, minimal);
}

2.把ht[0]复制并重新hash计算到ht[1]上

3.把ht[0]释放,ht[1]设置为ht[0]。

 

hash表的扩容与收缩

哈希表会自动在表的大小的二次方之间进行调整。

在没有bgSave或bgRewriteAOF命令时,负载因子大于1;或者有bgSave或bgRewriteAOF命令时,负载因子大于5;时执行

负载因子= 已保存/哈希表大小

渐进式rehash

 

跳跃表

skipList 

todo

整数集合

 

压缩表

 

对象

 

 

 

 

 

二:单机

原理

 

保存键值对的方法

 

过期策略

 

持久化机制

 

事件、redis初始化过程等其他

 

三:分布式

Sentinel

 

复制

 

集群

 

四:独立功能

 

发布订阅

 

事务

 

Lua脚本

 

排序

 

慢查询

 

 

 

1

1

1

1

1

1

1

1

1

1

1

脚本宝典总结

以上是脚本宝典为你收集整理的Redis设计实现-学习笔记全部内容,希望文章能够帮你解决Redis设计实现-学习笔记所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。