Redis

2 篇文章 0 订阅
1 篇文章 0 订阅

XYM个人博客对应篇章


最近看了下huangz1990写的《redis设计与实现》,做一下笔记。

内部数据结构

【sds】

struct sdshdr {
    int len;  // buf已用长度
    int free;  // 剩余空间
    char buf[]; // 保存字符串的char指针
}
  1. 简单动态字符串:主要用于存储redis里面的字符串对象,不过只有这个对象存储的是字符串的时候,才是sdsi,否则就是long类型。

  2. append操作:如果添加后长度超过SDS_MAX_PREALLOC(默认1M),则空间追加加多1M,否则为添加后长度的两倍,显然append的字符串不能超过1M。

  3. 可以看出一个明显的弊端,就是会有比较多的剩余空间,这部分空间是不会主动释放的,如果要释放需要调用对应的API(sdsRemoveFreeSpace)。

【双端链表】

// 节点
typedef struct listNode {
    struct listNode *prev;  //前指针
    struct listNode *next;  //后指针
    void *value;  //值
} listNode;

//链表
typedef struct list {
 listNode *head;  //头指针
 listNode *tail;  //尾指针
 unsigned long len;  //表长
 void *(*dup)(void *ptr);  //复制函数
 void (*free)(void *ptr);  //释放函数
 int (*match)(void *ptr, void *key);  //对比函数
} list;

因为是无类型指针存值,所以可以保存任意的值,双端队列的优势在于队头尾的操作方便,但是链表的查找效率显然是瓶颈,注意带查找(非头尾)的操作效率。

【字典】

typedef struct dict {
    dictType *type;  //处理特定类型的函数
    void *privdata;  //处理函数的私有数据
    dictht ht[2];  //用于rehash的滚动数组
    int rehashidx;  //rehash进度,-1标示未开始
    int iterators;  //正常使用的迭代器数量
} dict;

typedef struct dictht {
    dictEntry **table;  //桶,一个指针数组,取模后的值就是对应数组下标
    unsigned long size;  //指针数组大小
    unsigned long sizemask;  //取模掩码
    unsigned long used;  //节点数量
} dictht;

typedef struct dictEntry {
    void *key;  //键
    union {  //值
        void *val;
        uint64_t u64;
        int64_t s64;
    }
    struct dictEntry *next;  //下一个指针,取模后值相同的节点
} dictEntry;
  1. rehash是重新建一个大小于现在不同的表,将原来的hash表迁移到这个新的hash表的过程。

  2. rehash条件:
    (1) ratio = used/size >= 1,且dict_can_resize为真(持久化操作的时候一般为假)。
    (2) ratio > dict_force_resize_ratio(默认5)。

  3. rehash方式:渐进式(dictRehashStep, dictRehashMilliseconds)。

【跳跃表】

  1. 跳表是一种神奇的数据结构,依赖随机概率,但是效率却可以和平衡树相媲美,与平衡树相比,编程复杂度低,但是空间复杂度比较高。

  2. 不知道怎么加图进来,就不多说了,插入是随机插入某一层,查找从最上层开始找,查找,插入,删除都是log(N)(期望值,有最坏的情况,数据量越大越接近期望值)。

  3. 主要用来实现有序集合。

redis数据类型

typedef struct redisObject {
    unsigned type:4;  //类型
    unsigned notused:2;  //对齐位
    unsigned encoding:4;  //编码
    unsigned lru:22;  //lru时间
    int refcount;  //引用次数
    void *ptr;  //对象值
}
  1. redisObject 类型:字符串(整数,字符串), 列表(双端队列,压缩链表) 有序集合(跳跃表,压缩列表),哈希表(压缩列表,字典), 集合(字典,整数集合)。

  2. 字符串:REDIS_ENCODING_INT(long)和REDIS_ENCODING_RAW(sds), 除了能表示成long的,其他都保存为sds.

  3. 哈希表: 创建空白的哈希表的时候,是用压缩列表的,当哈希表的键或值长度大于某个值时(默认64),或者当节点数大于某个值(默认512)的时候,切换到哈希链表。

  4. 列表:先用压缩列表,达到某种条件(与哈希表条件类似),切换为双端链表。

  5. 阻塞:先记录相关阻塞信息,如阻塞时间,key,客户端等,server.db[i]->blocking_keys记录因为某个key被阻塞的客户端(多个)。

  6. 当push一个值的时候,server.ready_keys->ready_list添加这个key和对应的DB,并添加对应的值,注意解除阻塞为先阻塞先服务。

  7. 集合: 如果第一元素能用longlong表示,那么就用intset,否则用哈希表。

  8. 有序集合:先是ziplist,元素个数大于128时,或者元素长度大于64,转换成跳表。

事务

  1. redis事务SMEMBERS事务进队,DISCARD取消事务,EXEC执行事务,WATCH监视改动。

  2. watch命令用于事务执行之前监视任意数量的键,如果这些键被改变了,那么事务停止,直接返回失败。

  3. watch某个key时,redisDb.wathced_key存储这个key对应的watch客户端,当有对键的操作的时候,会触发查找key是否在watch_key里面,在就会改变客户端的REDIS_DIRTY_CAS,执行事务前会检查这个,如果有那么就回复空,表示失败。

  4. ACID: A(原子性),C(一致性),I(隔离性),D(持久性), redis里面只有保证了CI。

  5. lua: 使用lua执行redis函数,当执行自身的函数时,会创建一个伪客户端执行函数,返回给服务器。

  6. 订阅。

  7. 慢查询日志:日志滚动删除。

内部运作机制

  1. 键过期:保存在expires字典里面,存着每一个键的过期时间,使用定期删除(间隔一段时间清理一次)和惰性删除(用到这个键的时候,才删除)清除键值。

  2. RDB: 将内存镜像写入RDB文件(SAVE,BGSAVE),SAVE阻塞, BGSAVE 非阻塞。

  3. AOF: 将操作写入AOF日志(BGREWRITEAOF),rewrite. 异步每秒一次,高于BGSAVE。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值