redis中的hash也是我們使用中的高頻資料結構,它的構造基本上和程式語言中的HashTable,Dictionary大同小異,如果大家往後有什麼邏輯需要用
Dictionary存放的話,可以根據場景優先考慮下redis哦,起碼可以裝裝逼嘛,現在我預設你已經有裝逼的衝動了,開啟redis手冊,看看有哪些我們用得到
的裝逼方法。
一:常用方法
只要是一個資料結構,最基礎的永遠是CURD,redis中的insert和update,永遠只需要set來替代,比如下面的Hset,如下圖:
前面幾篇文章我都沒有挑選一個方法仔細講解,其實也沒什麼好講解的,就好似C#中的一個類的一個方法而已,知道傳遞一些啥引數就OK了,就比如要
說的HSet,它的格式如下:
接下來我在CentOS裡面操作一下,
[administrator@localhost redis-3.0.5]$ src/redis-cli 127.0.0.1:6379> clear 127.0.0.1:6379> hset person name jack (integer) 1 127.0.0.1:6379> hset person age 20 (integer) 1 127.0.0.1:6379> hset person sex famale (integer) 1 127.0.0.1:6379> hgetall person 1) "name" 2) "jack" 3) "age" 4) "20" 5) "sex" 6) "famale" 127.0.0.1:6379> hkeys person 1) "name" 2) "age" 3) "sex" 127.0.0.1:6379> hvals person 1) "jack" 2) "20" 3) "famale" 127.0.0.1:6379>
或許有人看了上面的console有一點疑惑,那就是前面有幾個引數,比如person,name啦,然後才是value,如果你看了第一篇的話,你大概就明白了,
其實在redis的這個層面,它永遠只有一個鍵,一個值,這個鍵永遠都是字串物件,也就是SDS物件,而值的種類就多了,有字串物件,有佇列物件,
還有這篇的hash物件,往後的有序集合物件等等,如果你還不明白的話,轉化為C#語言就是。
1 var person=new Dictionary<string,string>(); 2 person.Add("name","jack"); 3 ....
呼叫方法就是這麼的簡單,關鍵在於時不時的需要你看一看手冊,其實最重要的是瞭解下它在redis原始碼中的原理就好了。
二:探索原理
hash的原始碼是在dict.h原始碼裡面,列舉如下:
typedef struct dictEntry { void *key; union { void *val; uint64_t u64; int64_t s64; double d; } v; struct dictEntry *next; } dictEntry; typedef struct dictType { unsigned int (*hashFunction)(const void *key); void *(*keyDup)(void *privdata, const void *key); void *(*valDup)(void *privdata, const void *obj); int (*keyCompare)(void *privdata, const void *key1, const void *key2); void (*keyDestructor)(void *privdata, void *key); void (*valDestructor)(void *privdata, void *obj); } dictType; /* This is our hash table structure. Every dictionary has two of this as we * implement incremental rehashing, for the old to the new 0. */ typedef struct dictht { dictEntry **table; unsigned long size; unsigned long sizemask; unsigned long used; } dictht; typedef struct dict { dictType *type; void *privdata; dictht ht[2]; long rehashidx; /* rehashing not in progress if rehashidx == -1 */ int iterators; /* number of iterators currently running */ } dict; /* If safe is set to 1 this is a safe iterator, that means, you can call * dictAdd, dictFind, and other functions against the dictionary even while * iterating. Otherwise it is a non safe iterator, and only dictNext() * should be called while iterating. */ typedef struct dictIterator { dict *d; long index; int table, safe; dictEntry *entry, *nextEntry; /* unsafe iterator fingerprint for misuse detection. */ long long fingerprint; } dictIterator;
上面就是我們使用hash的原始碼資料結構,接下來我來擼一擼其中的邏輯關係。
1. dict結構
1 typedef struct dict { 2 dictType *type; 3 void *privdata; 4 dictht ht[2]; 5 long rehashidx; /* rehashing not in progress if rehashidx == -1 */ 6 int iterators; /* number of iterators currently running */ 7 } dict;
這個結構是hash的真正的底層資料結構,可以看到其中有5個屬性。
<1> dictType *type
可以看到它的型別是dictType,從上面你也可以看到,它是有列舉結構定義的,如下:
1 typedef struct dictType { 2 unsigned int (*hashFunction)(const void *key); 3 void *(*keyDup)(void *privdata, const void *key); 4 void *(*valDup)(void *privdata, const void *obj); 5 int (*keyCompare)(void *privdata, const void *key1, const void *key2); 6 void (*keyDestructor)(void *privdata, void *key); 7 void (*valDestructor)(void *privdata, void *obj); 8 } dictType;
從上面這個資料結構中你可以看到裡面都是一些方法,但是有一個非常重要的方法,那就是第一個hashFunction,可以看到它就是計算hash值的,
跟C#中的dictionary中求hash值一樣一樣的。
<2> dictht ht[2]
你可能會疑問,為什麼這個屬性是2個大小的陣列呢,其實正真使用的是ht[0],而ht[1]是用於擴容hash表時的暫存陣列,這一點也很奇葩,
同時也很精妙,redis為什麼會這麼做呢???仔細想想你可能會明白,擴容有兩種方法,要麼一次性擴容,要麼漸進性擴容,後面這種擴容是什
麼意思呢?就是我在擴容的同時不影響前端的CURD,我慢慢的把資料從ht[0]轉移到ht[1]中,同時rehashindex來記錄轉移的情況,當全部轉移
完成之後,將ht[1]改成ht[0]使用,就這麼簡單。
2. dicth結構
1 typedef struct dictht { 2 dictEntry **table; 3 unsigned long size; 4 unsigned long sizemask; 5 unsigned long used; 6 } dictht;
<1> dictEntry **table;
從上面這個結構體中,你可以看到一個非常重要的屬性: dictEntry **table, 其中table是一個陣列,陣列型別是dictEntry,既然是一個陣列,
那後面的三個屬性就好理解了,size是陣列的大小,sizemask和陣列求模有關,used記錄陣列中已使用的大小,現在我們把注意力放在dictEntry這
個陣列實體型別上面。
3. dictEntry結構
1 typedef struct dictEntry { 2 void *key; 3 union { 4 void *val; 5 uint64_t u64; 6 int64_t s64; 7 double d; 8 } v; 9 struct dictEntry *next; 10 } dictEntry;
從這個資料結構上面你可以看到有三個大屬性。
第一個就是: *key:它就是hash表中的key。
第二個就是: union的*val 就是hash的value。
第三個就是: *next就是為了防止hash衝突採用的掛鏈手段。
這個原理和C#中的Dictionary還是一樣一樣的。
不知道你看懂了沒有,如果總結上面描述的話,我可以畫出如下的hash結構圖。
好了,就此打住,去公司了。