Redis原始碼解析 06整數集合

2022-07-03 12:36:10 字數 2314 閱讀 1859

整數集合(intset)是集合鍵的底層實現之一,當乙個集合只包含整數值元素,並且這個集合的元素數量不多時,redis就會使用整數集合作為集合鍵的底層實現。

intset可以儲存型別為int16_t,int32_t,int64_t的整數值,並且保證集合中不會出現重複元素。

整數集合的結構體定義在intset.h中:

typedef struct intset  intset;

encoding表示intset中儲存的整數的編碼,共有三種編碼,分別是: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))

注意,編碼在儲存時是按照小端的方式儲存的,也就是說在大端系統中,還需要將實際的編碼值翻轉之後,才能儲存到encoding中,比如:

is->encoding =intrev32ifbe(intset_enc_int16); 其中intrev32ifbe表示將32位整數按照位元組進行翻轉。

length表示intset中儲存的整數個數。與encoding一樣,它也是按照小端的方式儲存的,在大端系統中,也需要將實際值翻轉之後,才能儲存到length中,比如:

is->length = intrev32ifbe(len-1)。

contents是柔性陣列成員,它是整數集合的底層實現:整數集合中的每個元素都是contents陣列的乙個陣列項,各個項在陣列中按值從小到大有序地排列,並且陣列中不包含任何重複項。

雖然contents的型別是int8_t,但它可以儲存型別為int16_t,int32_t,int64_t的整數值。可以將contents想象為一段連續的記憶體空間,該空間的實際大小是encoding*length。儲存在contents中的整數,也是按照小端的方式儲存的。

如果encoding屬性的值為intset_enc_int16,那contents中儲存的整數就是int16_t型別,範圍是[-32768,32767]。如果encoding屬性的值為intset_enc_int32,那contents中儲存的整數就是int32_t型別,範圍是[-2147483648, 2147483647]。如果encoding屬性的值為intset_enc_int64,那contents中儲存的整數就是int64_t型別,範圍是[-9223372036854775808,9223372036854775807]。

將乙個新元素新增到intset,且新元素的型別比整數集合現有所有元素的型別都要大時,整數集合需要先進行公升級,然後才能將新元素新增到整數集合裡面。公升級intset並新增新元素的**實現如下:

static intset *intsetupgradeandadd(intset *is, int64_t value)

該函式只有在公升級插入時才會呼叫,也就是說新值value超出了is現有的編碼範圍,因此,value要麼插入在索引0,要麼插入在索引is->length。

首先獲取is當前的編碼curenc,當前長度length,並根據value得到其合適的編碼newenc,newenc成為is在公升級插入value後的新編碼;

prepend用於公升級is時,定位contents中原有元素的新索引。如果value為正數,則value需要插入到索引is->length中,因此prepend為0,表明contents原有元素的索引值保持不變。注意這並不意味著contents的內容保持不變,因為編碼公升級了,每個元素所佔的記憶體空間也發生了變化,因此還需要重新設定每個索引的元素;

若value為負數,則說明value要插入到索引0中,因此prepend為1,表明contents原有元素需要後移乙個索引;

然後更新is的encoding為newenc,並呼叫intsetresize重新申請is的空間;根據新的編碼newenc,將is的contents中的原有元素,根據prepend的值重新放置;

最後根據prepend值,將value插入到0或length處;並更新is的length屬性。

注意,intset不支援降級操作,一旦對intset進行了公升級,編碼就會一直保持公升級後的狀態。比如乙個intset儲存了1,2,3,4四個數,這四個數使用編碼intset_enc_int16即可,後插入了4294967295,intset公升級編碼為intset_enc_int64。現在如果再把4294967295刪除了,則intset中,雖然還是1,2,3,4四個數,但是編碼依然保持為intset_enc_int64。

有關intset其他的**實現,可以參閱:

redis原始碼 整數集合

redis提供一種叫整數集合的資料結構,當資料中只包含整數,並且資料數量不多時,redis便會採用整數集合儲存 redis保證整數集合有以下幾個特性 所含元素全是整數,且不重複 內部元素有序,通常是會從小到大排序 內部編碼統一,盡可能採用合適的編碼儲存資料 當編碼不合適時,執行公升級操作 接下來會針...

Redis原始碼解析 字典結構

一般情況下,我們談到字典,難免要談到紅黑樹。但是redis這套字典庫並沒有使用該方案去實現,而是使用的是鍊錶,且整個 行數在1000行以內。所以這塊邏輯還是非常好分析的。我們可以想象下,如果使用普通的鍊錶去實現字典,那麼是不是整個資料都在一條鍊錶結構上呢?如果是這麼設計,插入和刪除操作是非常方便的,...

redis原始碼解析 跳躍表

定義 跳躍表是一種有序資料結構,它通過在每個節點中維護多個指向其他節點的指標,從而達到快速訪問節點的目的。跳躍表支援平均o logn 最壞o n 複雜度的節點查詢,大部分情況下,跳躍表的效率可以和平衡樹相媲美,並且因為跳躍表的實現比平衡樹要來得簡單,所以有不少程式都使用跳躍表來替代平衡樹。從圖中可以...