在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ 大數(shù)據(jù)/ Redis 數(shù)據(jù)結(jié)構(gòu) intset
Redis 數(shù)據(jù)淘汰機(jī)制
積分排行榜
小剖 Memcache
Redis 數(shù)據(jù)結(jié)構(gòu) intset
分布式鎖
從哪里開始讀起,怎么讀
Redis 數(shù)據(jù)結(jié)構(gòu) dict
不在浮沙筑高臺(tái)
Redis 集群(上)
Redis 監(jiān)視器
源碼閱讀工具
Redis 日志和斷言
內(nèi)存數(shù)據(jù)管理
Redis 數(shù)據(jù)結(jié)構(gòu)綜述
源碼日志
Web 服務(wù)器存儲(chǔ) session
消息中間件
Redis 與 Lua 腳本
什么樣的源代碼適合閱讀
Redis 數(shù)據(jù)結(jié)構(gòu) sds
Memcached slab 分配策略
訂閱發(fā)布機(jī)制
Redis 是如何提供服務(wù)的
Redis 事務(wù)機(jī)制
Redis 集群(下)
主從復(fù)制
Redis 應(yīng)用
RDB 持久化策略
Redis 數(shù)據(jù)遷移
Redis 事件驅(qū)動(dòng)詳解
初探 Redis
Redis 與 Memcache
AOF 持久化策略
Redis 數(shù)據(jù)結(jié)構(gòu) redisOb
作者簡(jiǎn)介
Redis 數(shù)據(jù)結(jié)構(gòu) ziplist
Redis 數(shù)據(jù)結(jié)構(gòu) skiplist
Redis 哨兵機(jī)制

Redis 數(shù)據(jù)結(jié)構(gòu) intset

intset 和 dict 都是 sadd 命令的底層數(shù)據(jù)結(jié)構(gòu),當(dāng)添加的所有數(shù)據(jù)都是整數(shù)時(shí),會(huì)使用前者;否則使用后者。特別的,當(dāng)遇到添加數(shù)據(jù)為字符串,即不能表示為整數(shù)時(shí),Redis 會(huì)把數(shù)據(jù)結(jié)構(gòu)轉(zhuǎn)換為 dict,即把 intset 中的數(shù)據(jù)全部搬遷到 dict。

本片展開的是 intset,dict 的文章可以參看之前寫的《Redis 數(shù)據(jù)結(jié)構(gòu) dict》。

intset 結(jié)構(gòu)體

intset 底層本質(zhì)是一個(gè)有序的、不重復(fù)的、整型的數(shù)組,支持不同類型整數(shù)。

typedef struct intset {
// 每個(gè)整數(shù)的類型
uint32_t encoding;
// intset 長(zhǎng)度
uint32_t length;
// 整數(shù)數(shù)組
int8_t contents[];
} intset;

結(jié)構(gòu)體中 intset.contents 數(shù)組沒有指定長(zhǎng)度,這樣是為了方便分配釋放內(nèi)存。encoding 能下面的三個(gè)值:分別是 16,32 和 64 位整數(shù):

/* Note that these encodings are ordered, so:
* INTSET_ENC_INT16 < INTSET_ENC_INT32 < INTSET_ENC_INT64. */
#define INTSET_ENC_INT16 (sizeof(int16_t))
#define INTSET_ENC_INT32 (sizeof(int32_t))
#define INTSET_ENC_INT64 (sizeof(int64_t))

intset 搜索

intset 是有序的整數(shù)數(shù)組,可以用二分搜索查找。

static uint8_t intsetSearch(intset *is, int64_t value, uint32_t *pos) {
   int min = 0, max = intrev32ifbe(is->length)-1, mid = -1;
   int64_t cur = -1;
   /* The value can never be found when the set is empty */
   // 集合為空
      if (intrev32ifbe(is->length) == 0) {
      if (pos) *pos = 0;
         return 0;
      } else {
      /* Check for the case where we know we cannot find the value,
      * but do know the insert position. */
      // value 比最大元素還大
      if (value > _intsetGet(is,intrev32ifbe(is->length)-1)) {
      if (pos) *pos = intrev32ifbe(is->length);
      return 0;
      // value 比最小元素還小
      } else if (value < _intsetGet(is,0)) {
      if (pos) *pos = 0;
      return 0;
      }
   }
   // 熟悉的二分查找
   while(max >= min) {
      mid = (min+max)/2;
      cur = _intsetGet(is,mid);
   if (value > cur) {
      min = mid+1;
   } else if (value < cur) {
      max = mid-1;
   } else {
      break;
      }
   }
   if (value == cur) {
   if (pos) *pos = mid;
      return 1;
   } else {
   if (pos) *pos = min;
      return 0;
   }
}

intset 插入

intset 實(shí)現(xiàn)中比較有意思的是插入算法部分。

/* Insert an integer in the intset */
intset *intsetAdd(intset *is, int64_t value, uint8_t *success) {
uint8_t valenc = _intsetValueEncoding(value);
uint32_t pos;
if (success) *success = 1;
    /* Upgrade encoding if necessary. If we need to upgrade, we know that
    * this value should be either appended (if > 0) or prepended (if < 0),
    * because it lies outside the range of existing values. */
    // 需要插入整數(shù)的所需內(nèi)存超出了原有集合整數(shù)的范圍,即內(nèi)存類型不同,
    // 則升級(jí)整數(shù)類型
    if (valenc > intrev32ifbe(is->encoding)) {
    /* This always succeeds, so we don't need to curry *success. */
        return intsetUpgradeAndAdd(is,value);
    // 正常,分配內(nèi)存,插入
    } else {
    // intset 內(nèi)部不允許重復(fù)
    /* Abort if the value is already present in the set.
    * This call will populate "pos" with the right position to insert
    * the value when it cannot be found. */
    if (intsetSearch(is,value,&pos)) {
    if (success) *success = 0;
        return is;
    }
    // realloc
    is = intsetResize(is,intrev32ifbe(is->length)+1);
    // 遷移內(nèi)存,騰出空間給新的數(shù)據(jù)。intsetMoveTail() 完成內(nèi)存遷移工作
    if (pos < intrev32ifbe(is->length)) intsetMoveTail(is,pos,pos+1);
    }
    // 在騰出的空間中設(shè)置新的數(shù)據(jù)
        _intsetSet(is,pos,value);
    // 更新intset size
        is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}

整數(shù)數(shù)組有可能遇到需要升級(jí)的時(shí)候,譬如往 int32_t 數(shù)組插入一個(gè) ing64_t 整數(shù)的時(shí)候。當(dāng)插入數(shù)據(jù)的內(nèi)存占用比原有數(shù)據(jù)大的時(shí)候,intsetUpgradeAndAdd() 會(huì)被調(diào)用。

/* Upgrades the intset to a larger encoding and inserts the given integer. */
static intset *intsetUpgradeAndAdd(intset *is, int64_t value) {
    uint8_t curenc = intrev32ifbe(is->encoding);
    uint8_t newenc = _intsetValueEncoding(value);
    int length = intrev32ifbe(is->length);
    // value<0 頭插,value>0 尾插
    int prepend = value < 0 ? 1 : 0;
    // realloc
    /* First set new encoding and resize */
    is->encoding = intrev32ifbe(newenc);
    is = intsetResize(is,intrev32ifbe(is->length)+1);
    // 逆向處理,防止數(shù)據(jù)被覆蓋,一般的插入排序步驟
    /* Upgrade back-to-front so we don't overwrite values.
    * Note that the "prepend" variable is used to make sure we have an empty
    * space at either the beginning or the end of the intset. */
    while(length--)
        _intsetSet(is,length+prepend,_intsetGetEncoded(is,length,curenc));
    // value<0 放在集合開頭,否則放在集合末尾。
    // 因?yàn)椋撕瘮?shù)是對(duì)整數(shù)所占內(nèi)存進(jìn)行升級(jí),意味著value 不是在集合中最大就是最??!
    /* Set the value at the beginning or the end. */
    if (prepend)
        _intsetSet(is,0,value);
    else
        _intsetSet(is,intrev32ifbe(is->length),value);
    // 更新set size
        is->length = intrev32ifbe(intrev32ifbe(is->length)+1);
    return is;
}