redis學習筆記二

來源:互聯網
上載者:User

標籤:

雙鏈表

雙端鏈表作為一種通用的資料結構,在Redis 內部使用得非常多:它既是Redis 列表結構的底
層實現之一,還被大量Redis 模組所使用,用於構建Redis 的其他功能。

應用

  • 實現Redis 的清單類型;

雙端鏈表還是Redis 清單類型的底層實現之一,當對清單類型的鍵進行操作——比如執行
RPUSH 、LPOP 或LLEN 等命令時,程式在底層操作的可能就是雙端鏈表。

Redis 列表使用兩種資料結構作為底層實現:

1. 雙端鏈表

2. 壓縮列表

因為雙端鏈表佔用的記憶體比壓縮列表要多,所以當建立新的列表鍵時,列表會優先考慮使用壓
縮列表作為底層實現,並且在有需要的時候,才從壓縮列表實現轉換到雙端鏈表實現。

  • Redis 自身功能的構建

  除了實現清單類型以外,雙端鏈表還被很多Redis 內部模組所應用:

  1. 事務模組使用雙端鏈表來按順序儲存輸入的命令;
  2. 伺服器模組使用雙端鏈表來儲存多個用戶端;
  3.  訂閱/發送模組使用雙端鏈表來儲存訂閱模式的多個用戶端;
  4.  事件模組使用雙端鏈表來儲存時間事件(time event);
  • 雙端鏈表的實現

雙端鏈表的實現由listNode 和list 兩個資料結構構成,展示了由這兩個結構組成的一
個雙端鏈表執行個體:

 

迭代器

Redis 為雙端鏈表實現了一個迭代器,這個迭代器可以從兩個方向對雙端鏈表進行迭代:
• 沿著節點的next 指標前進,從表頭向表尾迭代;
• 沿著節點的prev 指標前進,從表尾向表頭迭代;

typedef struct listIter {
// 下一節點
listNode *next;
// 迭代方向
int direction;

} listIter;

direction 記錄迭代應該從那裡開始:
• 如果值為adlist.h/AL_START_HEAD ,那麼迭代器執行從表頭到表尾的迭代;
• 如果值為adlist.h/AL_START_TAIL ,那麼迭代器執行從表尾到表頭的迭代;

 

字典

它是一種抽象資料結構,由一集索引值對(key-value pairs)組成,各個索引值對的鍵各不相同,程式可以將新的索引值對添加到字典中,或者基於鍵進行尋找、更新或刪除等操作。

字典的應用

1. 實現資料庫鍵空間(key space);

Redis 是一個索引值對資料庫,資料庫中的索引值對就由字典儲存:每個資料庫都有一個與之相對
應的字典,這個字典被稱之為鍵空間(key space)。

當使用者添加一個索引值對到資料庫時(不論索引值對是什麼類型),程式就將該索引值對添加到鍵空
間;當使用者從資料庫中刪除一個索引值對時,程式就會將這個索引值對從鍵空間中刪除;等等。

2. 用作Hash 類型鍵的其中一種底層實現;

Redis 的Hash 類型鍵使用以下兩種資料結構作為底層實現:
1. 字典;
2. 壓縮列表;

因為壓縮列表比字典更節省記憶體,所以程式在建立新Hash 鍵時,預設使用壓縮列表作為底層
實現,當有需要時,程式才會將底層實現從壓縮列錶轉換到字典。

Redis 選擇了高效且實現簡單的雜湊表作為字典的底層實現。

字典的實現:

typedef struct dictht {
// 雜湊表節點指標數組(俗稱桶,bucket)
dictEntry **table;
// 指標數組的大小
unsigned long size;
// 指標數組的長度掩碼,用於計算索引值
unsigned long sizemask;
// 雜湊表現有的節點數量
unsigned long used;
} dictht;

table 屬性是一個數組,數組的每個元素都是一個指向dictEntry 結構的指標。

每個dictEntry 都儲存著一個索引值對,以及一個指向另一個dictEntry 結構的指標:

/*
* 雜湊表節點
*/
typedef struct dictEntry {
// 鍵
void *key;
1.3. 字典15
Redis 設計與實現, 第一版
// 值
union {
void *val;
uint64_t u64;
int64_t s64;
} v;
// 鏈往後繼節點
struct dictEntry *next;
} dictEntry;

在的字典樣本中,字典雖然建立了兩個雜湊表,但正在使用的只有0 號雜湊表,這說明字典未進行rehash 狀態。

碰撞的處理方法:

假設現在有三個借點的雜湊表如下:

對於一個新的索引值對key4 和value4 ,如果key4 的雜湊值和key1 的雜湊值相同,那麼它們
將在雜湊表的0 號索引上發生碰撞。通過將key4-value4 和key1-value1 兩個索引值對用鏈表串連起來,就可以解決碰撞的問題:

添加新索引值對時觸發了rehash 操作:

對於使用鏈地址法來解決碰撞問題的雜湊表dictht 來說,雜湊表的效能依賴於它的大小(size
屬性)和它所儲存的節點的數量(used 屬性)之間的比率:
• 比率在1:1 時,雜湊表的效能最好;
• 如果節點數量比雜湊表的大小要大很多的話,那麼雜湊表就會退化成多個鏈表,雜湊表
本身的效能優勢就不再存在;

 

為了在字典的索引值對不斷增多的情況下保持良好的效能,字典需要對所使用的雜湊表(ht[0])進行rehash 操作:在不修改任何索引值對的情況下,對雜湊表進行擴容,盡量將比率維持在1:1

左右。

dictAdd 在每次向字典添加新索引值對之前,都會對雜湊表ht[0] 進行檢查,對於ht[0] 的
size 和used 屬性,如果它們之間的比率ratio = used / size 滿足以下任何一個條件的話,
rehash 過程就會被啟用:
1. 自然rehash :ratio >= 1 ,且變數dict_can_resize 為真。
2. 強制rehash : ratio 大於變數dict_force_resize_ratio (目前版本中,
dict_force_resize_ratio 的值為5 )。

Note: 什麼時候dict_can_resize 會為假?

一個資料庫就是一個字典,資料庫裡的雜湊類型鍵
也是一個字典,當Redis 使用子進程對資料庫執行後台持久化任務時(比如執行BGSAVE
或BGREWRITEAOF 時), 為了最大化地利用系統的copy on write 機制, 程式會暫時將
dict_can_resize 設為假,避免執行自然rehash ,從而減少程式對記憶體的觸碰(touch)。
當持久化任務完成之後,dict_can_resize 會重新被設為真。

字典的rehash 操作實際上就是執行以下任務:
1. 建立一個比ht[0]->table 更大的ht[1]->table ;
2. 將ht[0]->table 中的所有索引值對遷移到ht[1]->table ;
3. 將原有ht[0] 的資料清空,並將ht[1] 替換為新的ht[0] ;
經過以上步驟之後,程式就在不改變原有索引值對資料的基礎上,增大了雜湊表的大小。

漸進式rehash

漸進式rehash 主要由_dictRehashStepdictRehashMilliseconds 兩個函數進行:
• _dictRehashStep 用於對資料庫字典、以及雜湊鍵的字典進行被動rehash ;
• dictRehashMilliseconds 則由Redis 伺服器常規任務程式(server cron job)執行,用
於對資料庫字典進行主動rehash ;

字典的收縮

收縮rehash 和上面展示的擴充rehash 的操作幾乎一樣,它執行以下步驟:
1. 建立一個比ht[0]->table 小的ht[1]->table ;
2. 將ht[0]->table 中的所有索引值對遷移到ht[1]->table ;
3. 將原有ht[0] 的資料清空,並將ht[1] 替換為新的ht[0] ;
擴充rehash 和收縮rehash 執行完全相同的過程,一個rehash 是擴充還是收縮字典,關鍵在於
新分配的ht[1]->table 的大小:

字典的收縮規則由redis.c/htNeedsResize 函數定義:
/*
* 檢查字典的使用率是否低於系統允許的最小比率
**
是的話返回1 ,否則返回0 。
*/
int htNeedsResize(dict *dict) {
long long size, used;
// 雜湊表已用節點數量
size = dictSlots(dict);
// 雜湊表大小
used = dictSize(dict);
// 當雜湊表的大小大於DICT_HT_INITIAL_SIZE
// 並且字典的填充率低於REDIS_HT_MINFILL 時
// 返回1
return (size && used && size > DICT_HT_INITIAL_SIZE &&
(used*100/size < REDIS_HT_MINFILL));
}

在預設情況下,REDIS_HT_MINFILL 的值為10 ,也即是說,當字典的填充率低於10% 時,
序就可以對這個字典進行收縮操作了。

字典的其他動作和字典的迭代.....(略)

跳躍表(很牛逼的一種表)....(略)

操作有ZADD ZRANGE等。

 

redis學習筆記二

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.