形象化設計模式實戰
hello!架構
redis命令原始碼解析
在redis之字串命令原始碼解析(一)中講了get的簡單實現,並沒有對如何取到資料做深入分析,這裡將深入。
(一)中說set test "hello redis",「hello redis」會最終儲存在robj中,redisobject是redis的核心,資料庫的每個鍵、值,以及redis本身處理的引數都表示為這種資料型別,其結構如下:
/* the actual redis object */
/* * redis 物件
*/#define redis_lru_bits 24
#define redis_lru_clock_max ((1
#define redis_lru_clock_resolution 1000 /* lru clock resolution in ms */
typedef struct redisobject robj;
物件型別有:
#define redis_string 0 // 字串
#define redis_list 1 // 列表
#define redis_set 2 // 集合
#define redis_zset 3 // 有序集
#define redis_hash 4 // 雜湊表
物件編碼有:
#define redis_encoding_raw 0 // 編碼為字串
#define redis_encoding_int 1 // 編碼為整數
#define redis_encoding_ht 2 // 編碼為雜湊表
#define redis_encoding_zipmap 3 // 編碼為zipmap
#define redis_encoding_linkedlist 4 // 編碼為雙端鍊錶
#define redis_encoding_ziplist 5 // 編碼為壓縮列表
#define redis_encoding_intset 6 // 編碼為整數集合
#define redis_encoding_skiplist 7 // 編碼為跳躍表
redis使用的是高效且實現簡單的雜湊作為字典的底層實現。
dict.h中定義如下
:
/*雜湊表dictht的結構:* 字典
*/typedef struct dict dict;
/* this is our hash table structure. every dictionary has two of this as we雜湊表陣列dictentry的結構:* implement incremental rehashing, for the old to the new table. */
/* * 雜湊表
* * 每個字典都使用兩個雜湊表,從而實現漸進式 rehash 。
*/typedef struct dictht dictht;
/*那麼乙個dict可以**表示為:* 雜湊表節點
*/typedef struct dictentry v;
// 指向下個雜湊表節點,形成鍊錶
struct dictentry *next;
} dictentry;
由圖可清晰地看出redis字典雜湊表所使用的雜湊碰撞解決方法是鏈位址法,這個方法就是使用鍊錶將多個雜湊值相同的節點串連在一起,從而解決衝突問題。
set命令最終會呼叫dict.c中的dictadd方法將test => "hello redis" 儲存到字典中
/* add an element to the target hash table */整個set可簡略如下圖(此圖省去了許多其它操作):/* * 嘗試將給定鍵值對新增到字典中
* * 只有給定鍵 key 不存在於字典時,新增操作才會成功
* * 新增成功返回 dict_ok ,失敗返回 dict_err
* * 最壞 t = o(n) ,平灘 o(1)
*/int dictadd(dict *d, void *key, void *val)
從圖中你會發現,其實key的過期時間就相當於是key的另乙個val,儲存在另乙個dict中,簡單地說就是有兩個dict,乙個是key=>value,乙個是key=>expire。
dict有兩個ht,就是每個字典有兩個雜湊表,為毛要有兩個,其作用是對dict進行擴容和收縮,因為如果節點數量比雜湊表的大小要大很多的話,那麼雜湊表就會退化成多個鍊錶,雜湊表本身的效能優勢就不再存在。
dict.c中的_dictexpandifneeded方法對雜湊表何時可rehash作了判斷:
// 一下兩個條件之一為真時,對字典進行擴充套件
// 1)字典已使用節點數和字典大小之間的比率接近 1:1
// 並且 dict_can_resize 為真
// 2)已使用節點數和字典大小之間的比率超過 dict_force_resize_ratio(預設值為5)
if (d->ht[0].used >= d->ht[0].size &&
(dict_can_resize ||
d->ht[0].used/d->ht[0].size > dict_force_resize_ratio))
rehash的**這裡不貼出,因為實現簡單,大致的過程是
1. 釋放ht[0] 的空間;
2. 用ht[1] 來代替ht[0] ,使原來的ht[1] 成為新的ht[0] ;
3. 建立乙個新的空雜湊表,並將它設定為ht[1] ;
4. 將字典的rehashidx 屬性設定為-1 ,標識rehash 已停止;
但我在看源**時,發現並不是一將rehashidx設為0就進行rehash操作的,而是當再次dictadd時,才dictrehash(d,1),第二個引數是1,也就是說每次rehash只會對單個索引上的節點進行遷移,這種做法幾乎不會消耗什麼時間,客戶端可以快速的得到響應。當然這種除了這種方式進行rehash外,redis還有個定時任務呼叫dictrehashmilliseconds方法,在規定的時間內,盡可能地對資料庫字典中那些需要rehash的字典進行rehash,從而加速rehash的程序。
現在我知道redis並不是一下子就rehash完成,而是需要一定時間的,那麼如果客戶端在這段時間內向redis傳送get set del請求,那redis會如何處理,從而保證資料的完整和正確呢?
• 因為在rehash 時,字典會同時使用兩個雜湊表,所以在這期間的所有查詢、刪除等操作,除了在ht[0] 上進行,還需要在ht[1] 上進行。
• 在執行新增操作時,新的節點會直接新增到ht[1] 而不是ht[0] ,這樣保證ht[0] 的節點數量在整個rehash 過程中都只減不增。
Redis 字串原始碼原理
1 redis的字串結構被設計成乙個 sds 結構,字串實際內容是被存放在乙個陣列中,如下表 structsds 當字串的大小超出當前分配的capacity大小時,陣列將擴容,分配更大的陣列,將舊的陣列拷貝到新陣列中,再將增加到字串新增進去。2 embstr 與raw 1 redis的字串的儲存方式...
Redis原始碼閱讀 sds字串實現
從開始工作就開始使用redis,也有一段時間了,但都只是停留在使用階段,沒有往更深的角度探索,每次想讀原始碼都止步在閱讀書籍上,因為看完書很快又忘了,這次逼自己先讀 因為個人覺得寫作需要閱讀文本來增強靈感,那麼寫 的,就閱讀更多 來增強靈感吧。redis的實現原理,在 redis設計與實現 一書中講...
Redis資料之字串命令
賦值 set key value 取值 當鍵不存在時會返回空 get key 向尾部追加值 獲取字串長度 strlen命令返回鍵值的長度,如果鍵不存在則返回0 strlen key 獲得 設定多個鍵值 mget mset 與get set 相似,不過mget mset 可以同時獲得 設定多個鍵的鍵值...