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;