• Redis原理:IntSet


    (笔记总结自b站黑马程序员课程)

    一、结构

    IntSet是Redis中set集合的一种实现方式,基于整数数组来实现,并且具备长度可变、有序等特征。 结构如下:

    1. typedef struct intset {
    2. uint32_t encoding; //编码方式
    3. uint32_t length; //元素个数
    4. int8_t contents[]; //整数数组
    5. } intset;

    注意整数数组记录的是每一个数的起始地址,决定数字长度是编码方式。

    其中的encoding包含三种模式,表示存储的整数大小不同:

    1. #define INTSET_ENC_INT16 (sizeof(int16_t))
    2. #define INTSET_ENC_INT32 (sizeof(int32_t))
    3. #define INTSET_ENC_INT64 (sizeof(int64_t))

    为了方便查找,Redis会将intset中所有的整数按照升序依次保存在contents数组中,结构如图:

    现在,数组中每个数字都在int16_t的范围内,因此采用的编码方式是INTSET_ENC_INT16,每部分占用的字节大小为:

    ①encoding:4字节(固定)

    ②length:4字节(固定)

    ③contents:2字节 * 3 = 6字节  

    二、扩展 

    我们向该其中添加一个数字:50000,这个数字超出了int16_t的范围,intset会自动升级编码方式到合适的大小。 以当前案例来说流程如下:

    • 升级编码为INTSET_ENC_INT32, 每个整数占4字节,并按照新的编码方式及元素个数扩容数组

    • 倒序依次将数组中的元素拷贝到扩容后的正确位置(倒序可以规避数据覆盖问题)

    • 将待添加的元素放入数组末尾

    • 最后,将inset的encoding属性改为INTSET_ENC_INT32,将length属性改为4

    三、源码

    源码理解就行,注意插入数据底层用到了二分查找的算法。

    插入新数据:

    1. /* Insert an integer in the intset */
    2. intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
    3. //获取当前值编码
    4. uint8_t valenc = _intsetValueEncoding(value);
    5. //要插入的位置
    6. uint32_t pos;
    7. if (success) *success = 1;
    8. /* Upgrade encoding if necessary. If we need to upgrade, we know that
    9. * this value should be either appended (if > 0) or prepended (if < 0),
    10. * because it lies outside the range of existing values. */
    11. //判断编码是否超过当前intset的编码
    12. if (valenc > intrev32ifbe(is->encoding)) {
    13. //超出编码,需要升级
    14. /* This always succeeds, so we don't need to curry *success. */
    15. return intsetUpgradeAndAdd(is,value);
    16. } else {
    17. //在当前intset中查找值与value一样元素的角标pos
    18. /* Abort if the value is already present in the set.
    19. * This call will populate "pos" with the right position to insert
    20. * the value when it cannot be found. */
    21. if (intsetSearch(is,value,&pos)) {
    22. if (success) *success = 0; //如果找到了,则无需插入,直接结束并返回失败
    23. return is;
    24. }
    25. //数组扩容
    26. is = intsetResize(is,intrev32ifbe(is->length)+1);
    27. //移动数组中pos之后的元素到pos+1,给新元素腾出空间
    28. if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
    29. }
    30. //插入新元素
    31. _intsetSet(is,pos,value);
    32. //重置元素长度
    33. is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    34. return is;
    35. }

    升级编码方式:

    1. /* Upgrades the intset to a larger encoding and inserts the given integer. */
    2. static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
    3. //获取当前inset编码
    4. uint8_t curenc = intrev32ifbe(is->encoding);
    5. //获取新编码
    6. uint8_t newenc = _intsetValueEncoding(value);
    7. int length = intrev32ifbe(is->length); //获取元素个数
    8. //判断新元素是大于0还是小于0,小于0插入队首,大于0插入队尾
    9. int prepend = value < 0 ? 1 : 0;
    10. //重置编码为新编码
    11. /* First set new encoding and resize */
    12. is->encoding = intrev32ifbe(newenc);
    13. //重置数组大小
    14. is = intsetResize(is,intrev32ifbe(is->length)+1);
    15. /* Upgrade back-to-front so we don't overwrite values.
    16. * Note that the "prepend" variable is used to make sure we have an empty
    17. * space at either the beginning or the end of the intset. */
    18. //倒序遍历,诸葛搬运元素到新的位置
    19. while(length--)
    20. _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));
    21. /* Set the value at the beginning or the end. */
    22. //插入新元素,prepend决定是队首还是队尾
    23. if (prepend)
    24. _intsetSet(is,0,value);
    25. else
    26. _intsetSet(is,intrev32ifbe(is->length),value);
    27. //修改数组长度
    28. is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    29. return is;
    30. }

    四、总结

    Intset可以看做是特殊的整数数组,具备一些特点:

    • Redis会确保Intset中的元素唯一、有序

    • 具备类型升级机制,可以节省内存空间

    • 底层采用二分查找方式来查询

    注意:一般适合在数据量不是很多的情况下使用 

  • 相关阅读:
    机器学习笔记之概率图模型(五)马尔可夫随机场的结构表示
    Splunk serverclass 没有生效
    iOS CGRect CGPoint NSRange等结构体的NSLog打印输出
    动态规划算法
    PostgreSQL位居第四,SQL Server表现差强人意,11月份数据库排行榜出炉
    JS 原型原型链
    通胀飙升、加息,投资者需要好的投资标的
    终于,老Mac可以跨级安装macOS Ventura了
    若依分离版 后端自定义分页
    GIT相关内容总结
  • 原文地址:https://blog.csdn.net/m0_64694079/article/details/132768863