Redis有哪些數據結構?
使用過Redis分佈式鎖麼,它是什麼回事?
假如Redis裏面有1億個key,其中有10w個key是以某個固定的已知的前綴開頭的,若是將它們所有找出來?
使用過Redis作異步隊列麼,你是怎麼用的?
Redis 是速度很是快的非關係型(NoSQL)內存鍵值數據庫,能夠存儲鍵和五種不一樣類型的值之間的映射。
鍵的類型只能爲字符串
值支持的五種類型數據類型爲:字符串、列表、集合、有序集合、散列表。
Redis 支持不少特性,例如將內存中的數據持久化到硬盤中,使用複製來擴展讀性能,使用分片來擴展寫性能。
數據類型能夠存儲的值操做STRING字符串、整數或者浮點數對整個字符串或者字符串的其中一部分執行操做</br> 對整數和浮點數執行自增或者自減操做LIST列表從兩端壓入或者彈出元素</br> 讀取單個或者多個元素</br> 進行修剪,只保留一個範圍內的元素SET無序集合添加、獲取、移除單個元素</br> 檢查一個元素是否存在於集合中</br> 計算交集、並集、差集</br> 從集合裏面隨機獲取元素HASH包含鍵值對的無序散列表添加、獲取、移除單個鍵值對</br> 獲取全部鍵值對</br> 檢查某個鍵是否存在ZSET有序集合添加、獲取、刪除元素</br> 根據分值範圍或者成員來獲取元素</br> 計算一個鍵的排名
image.png
> set hello world
OK
> get hello
"world"
> del hello
(integer) 1
> get hello
(nil)
image.png
> rpush list-key item
(integer) 1
> rpush list-key item2
(integer) 2
> rpush list-key item
(integer) 3
> lrange list-key 0 -1
1) "item"
2) "item2"
3) "item"
> lindex list-key 1
"item2"
> lpop list-key
"item"
> lrange list-key 0 -1
1) "item2"
2) "item"
image.png
> sadd set-key item
(integer) 1
> sadd set-key item2
(integer) 1
> sadd set-key item3
(integer) 1
> sadd set-key item
(integer) 0
> smembers set-key
1) "item"
2) "item2"
3) "item3"
> sismember set-key item4
(integer) 0
> sismember set-key item
(integer) 1
> srem set-key item2
(integer) 1
> srem set-key item2
(integer) 0
> smembers set-key
1) "item"
2) "item3"
image.png
> hset hash-key sub-key1 value1
(integer) 1
> hset hash-key sub-key2 value2
(integer) 1
> hset hash-key sub-key1 value1
(integer) 0
> hgetall hash-key
1) "sub-key1"
2) "value1"
3) "sub-key2"
4) "value2"
> hdel hash-key sub-key2
(integer) 1
> hdel hash-key sub-key2
(integer) 0
> hget hash-key sub-key1
"value1"
> hgetall hash-key
1) "sub-key1"
2) "value1"
image.png
> zadd zset-key 728 member1
(integer) 1
> zadd zset-key 982 member0
(integer) 1
> zadd zset-key 982 member0
(integer) 0
> zrange zset-key 0 -1 withscores
1) "member1"
2) "728"
3) "member0"
4) "982"
> zrangebyscore zset-key 0 800 withscores
1) "member1"
2) "728"
> zrem zset-key member1
(integer) 1
> zrem zset-key member1
(integer) 0
> zrange zset-key 0 -1 withscores
1) "member0"
2) "982"
zset是set的一個升級版本,他在set的基礎上增長了一個順序屬性,這一屬性在添加修改元素的時候能夠指定,每次指定後,zset會自動從新按新的值調整順序。 能夠對指定鍵的值進行排序權重的設定,它應用排名模塊比較多。
跳躍表(shiplist)是實現sortset(有序集合)的底層數據結構之一
另外還能夠用 Sorted Sets 來作帶權重的隊列,好比普通消息的 score 爲1,重要消息的 score 爲2,而後工做線程能夠選擇按 score的倒序來獲取工做任務,讓重要的任務優先執行。
dictht 是一個散列表結構,使用拉鍊法保存哈希衝突的 dictEntry。
/* This is our hash table structure. Every dictionary has two of this as we
* implement incremental rehashing, for the old to the new table. */
typedef struct dictht {
dictEntry **table;
unsigned long size;
unsigned long sizemask;
unsigned long used;
} dictht;
typedef struct dictEntry {
void *key;
union {
void *val;
uint64_t u64;
int64_t s64;
double d;
} v;
struct dictEntry *next;
} dictEntry;
Redis 的字典 dict 中包含兩個哈希表 dictht,這是爲了方便進行 rehash 操做。
在擴容時,將其中一個 dictht 上的鍵值對 rehash 到另外一個 dictht 上面,完成以後釋放空間並交換兩個 dictht 的角色。
typedef struct dict {
dictType *type;
void *privdata;
dictht ht[2];
long rehashidx; /* rehashing not in progress if rehashidx == -1 */
unsigned long iterators; /* number of iterators currently running */
} dict;
rehash 操做不是一次性完成,而是採用漸進方式,這是爲了不一次性執行過多的 rehash 操做給服務器帶來過大的負擔。
漸進式 rehash 經過記錄 dict 的 rehashidx 完成,它從 0 開始,而後每執行一次 rehash 都會遞增。例如在一次 rehash 中,要把 dict[0] rehash 到 dict[1],這一次會把 dict[0] 上 table[rehashidx] 的鍵值對 rehash 到 dict[1] 上,dict[0] 的 table[rehashidx] 指向 null,並令 rehashidx++。
在 rehash 期間,每次對字典執行添加、刪除、查找或者更新操做時,都會執行一次漸進式 rehash。
採用漸進式 rehash 會致使字典中的數據分散在兩個 dictht 上,所以對字典的操做也須要到對應的 dictht 去執行。
/* Performs N steps of incremental rehashing. Returns 1 if there are still
* keys to move from the old to the new hash table, otherwise 0 is returned.
*
* Note that a rehashing step consists in moving a bucket (that may have more
* than one key as we use chaining) from the old to the new hash table, however
* since part of the hash table may be composed of empty spaces, it is not
* guaranteed that this function will rehash even a single bucket, since it
* will visit at max N*10 empty buckets in total, otherwise the amount of
* work it does would be unbound and the function may block for a long time. */
int dictRehash(dict *d, int n) {
int empty_visits = n * 10; /* Max number of empty buckets to visit. */
if (!dictIsRehashing(d)) return 0;
while (n-- && d->ht[0].used != 0) {
dictEntry *de, *nextde;
/* Note that rehashidx can't overflow as we are sure there are more
* elements because ht[0].used != 0 */
assert(d->ht[0].size > (unsigned long) d->rehashidx);
while (d->ht[0].table[d->rehashidx] == NULL) {
d->rehashidx++;
if (--empty_visits == 0) return 1;
}
de = d->ht[0].table[d->rehashidx];
/* Move all the keys in this bucket from the old to the new hash HT */
while (de) {
uint64_t h;
nextde = de->next;
/* Get the index in the new hash table */
h = dictHashKey(d, de->key) & d->ht[1].sizemask;
de->next = d->ht[1].table[h];
d->ht[1].table[h] = de;
d->ht[0].used--;
d->ht[1].used++;
de = nextde;
}
d->ht[0].table[d->rehashidx] = NULL;
d->rehashidx++;
}
/* Check if we already rehashed the whole table... */
if (d->ht[0].used == 0) {
zfree(d->ht[0].table);
d->ht[0] = d->ht[1];
_dictReset(&d->ht[1]);
d->rehashidx = -1;