Redis数据结构

Redis数据结构

根据官方文档内容介绍

五种基本数据结构 String, List, Hash, Set, Set

String

key和value最大都是512MB,推荐设置成“user:1000”这样有意义的key。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
set <key> <value>
set key value nx 如果已经存在,则设置失败,返回nil
set key value xx 如果存在,才能设置成功,返回OK
set key value ex 10 过期时间

get key

expire key 5 设置key的过期时间

ttl key 查看key的time to live(默认单位:秒)

del key 删除键

pexpire

pttl
1
2
3
4
5
6
7
8
9
10
计数器
> set counter 100
OK
> incr counter
(integer) 101
> incr counter
(integer) 102
> incrby counter 50
(integer) 152

type key
查看key的value是什么类型,如果key不存在返回none

多设置,多获取(避免多次单次设置的网络延迟)

可以一次性获取多个值,一次性设置多个值

1
2
mset
mget

List

创建List,左右添加元素

lpush key <元素列表>
rpush

阻塞获取元素

lpop,rpop,blpop,brpop

blpop key 5
默认时间是秒
也可以阻塞获取多个key列表的元素
返回的是一个二元组,存在元素的key和key中的value,如果时间到了尚未获取到元素,则返回nil

将source列表中的最左侧或者最右侧移动到dest的末尾,过去是lpoprpush指令
lmove source dest <right|left> <right|left>

Hash

hash的结构 key, pair<field,value>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
hset user:1000 username antirez birthyear 1977 verified 1
(integer) 3

hget user:1000 username
"antirez"

hget user:1000 birthyear
"1977"

hgetall user:1000
1) "username"
2) "antirez"
3) "birthyear"
4) "1977"
5) "verified"
6) "1"

可放入hash的field数量并没有做实际限制,除了物理上可用的内存。

hset,hget,hgetall

hmget 可以获取多个field的值

增加值类型位数值 field 的数值
hincrby

Set

基于哈希表实现的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
# 添加set元素,创建set
sadd myset 1 2 3
(integer) 3
# 查看myset的元素
smembers myset
1. 3
2. 1
3. 2
# 判断3是否是myset元素
sismember myset 3
(integer) 1
sismember myset 30
(integer) 0

#创建集合news:1000:tags
sadd news:1000:tags 1 2 5 77
(integer) 4
# 查看news:1000:tags的元素
smembers news:1000:tags


sadd tag:1:news 1000
(integer) 1
sadd tag:2:news 1000
(integer) 1
sadd tag:5:news 1000
(integer) 1
sadd tag:77:news 1000
(integer) 1


#新建一个52张扑克牌的集合 club, diamond, spade, heart 代表♣️,♦️,♠️,♥️
sadd deck C1 C2 C3 C4 C5 C6 C7 C8 C9 C10 CJ CQ CK
D1 D2 D3 D4 D5 D6 D7 D8 D9 D10 DJ DQ DK H1 H2 H3
H4 H5 H6 H7 H8 H9 H10 HJ HQ HK S1 S2 S3 S4 S5 S6
S7 S8 S9 S10 SJ SQ SK


#将deck复制给 game:1:deck
sunionstore game:1:deck deck


#获取元素个数
scard game:1:deck

#随机取出一个元素,并删除
spop game:1:deck

#随机获取一个元素,不删除,所以可能多次使用会出现重复的元素
srandmember game:1:deck

Sorted Set

ZSet数据结构

有序集合是通过两种数据结构实现的

压缩列表 ziplist

ziplist是为了提高存储效率而设计的一种特殊编码的双向链表。它可以存储字符串或者整数,存储整数时是采用整数的二进制而不是字符串形式存储。它能在O(1)的时间复杂度下完成list两端的push和pop操作。但是因为每次操作都需要重新分配ziplist的内存,所以实际复杂度和ziplist的内存使用量相关。

跳跃表 zSkiplist

跳跃表的性能可以保证在查找,删除,添加等操作的时候在对数期望时间内完成,这个性能是可以和平衡树来相比较的,而且在实现方面比平衡树要优雅,这是采用跳跃表的主要原因。跳跃表的复杂度是O(log(n))。

Set中的元素并不是有序的,但是Sorted Set中的每个元素和一个浮点数score相联系,有点像Hash和Set。

排序规则:
(1)如果A.score is > B.score,那么 A>B
(2)如果A.score is == B.score,那么字典序大的A会A>B

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
zadd <zset_name> score key
zadd hackers 1940 "Alan Kay"

#也能添加多个entry,形如 score-value的形式
zadd hackers 1912 "Alan Turing" 1969 "Linus Torvalds"


#返回排序的结果,默认是score从小到大
zrange hackers 0 -1
#Alan Turing
#Claude Shannon
#Hedy Lamarr
#Alan Kay
#Sophie Wilson
#Linus Torvalds

# 返回排序结构,并包含数值,出身年份不一定准确,是假数据
zrange hackers 0 -1 withscores
Alan Turing
1912
Claude Shannon
1916
Hedy Lamarr
1916
Alan Kay
1940
Sophie Wilson
1957
Linus Torvalds
1969


#也可以逆序排序,实现score从大到小的排序
zrevrange hackers 0 -1

zrevrange hackers 0 -1 withscores
Linus Torvalds
1969
Sophie Wilson
1957
Alan Kay
1940
Hedy Lamarr
1916
Claude Shannon
1916
Alan Turing
1912

范围查询

return all the elements with a score between negative infinity and 1950

1
2
3
4
5
6
> zrangebyscore hackers -inf 1950
1) "Alan Turing"
2) "Hedy Lamarr"
3) "Claude Shannon"
4) "Alan Kay"
5) "Anita Borg"

范围删除

1
2
# (1940,1960) ,开区间
zremrangebyscore hackers 1940 1960

获得zset中的元素排序

1
zrank hackers "Anita Borg"

字典排序

1
2
3
4
5
6
7
8
zadd hackers 0 "Alan Kay" 0 "Sophie Wilson" 0 "Richard Stallman" 0
"Anita Borg" 0 "Yukihiro Matsumoto" 0 "Hedy Lamarr" 0 "Claude Shannon"
0 "Linus Torvalds" 0 "Alan Turing"

zrange hackers 0 -1

# zrangebylex
ZRANGEBYLEX key min max [LIMIT offset count]

zrangebylex的样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
redis> ZADD myzset 0 a 0 b 0 c 0 d 0 e 0 f 0 g
(integer) 7
redis> ZRANGEBYLEX myzset - [c
1) "a"
2) "b"
3) "c"
redis> ZRANGEBYLEX myzset - (c
1) "a"
2) "b"
redis> ZRANGEBYLEX myzset [aaa (g
1) "b"
2) "c"
3) "d"
4) "e"
5) "f"

BITMAP

  • 可以实现统计365天用户的登陆天数

缓存穿透 击穿 雪崩 污染

穿透

穿透:redis没有数据,mysql也没有数据

  • 解决方案

(1)缓存空值

如一个不存在的userID,这个id在缓存和数据库中都不存在,则可以缓存一个空值,下次再查缓存直接返回空值;(防击穿)

(2)布隆过滤器

通过bloom filter算法来存储合法key,得益于算法超高的压缩效率,只需占用极小的空间就能存储大量key值。

击穿

击穿:redis没有数据,mysql有数据

  • 现象:数据key过期,导致多个对同一个key的查询压力给到服务器侧
  • 解决方案

(1)接口限流与熔断,降级

重要的接口一定要做好限流策略,防止用户恶意刷接口,同时要降级准备,当接口中的某些 服务 不可用时候,进行熔断,失败快速返回机制。

(2)加互斥锁

雪崩

很多数据过期,导致查询压力大量给到服务器侧

  • 解决方案

(1)分散缓存时间

将缓存时间分散,比如在原有的实效时间上加一个随机数,例如不同key的过期时间,可以设置为10分1秒过期,10分23秒过期。

过期时间分散,对于热点数据,过期时间尽量设置得长一点,冷门数据过期时间可以短一点,这样留足够的时间给redis进行数据持久化和主从同步。

(2)使用缓存集群

使用缓存集群,避免单机宕机造成的缓存雪崩。

污染

指数据只被访问几次,但会长时间驻留在缓存中,消耗缓存空间
需要设置key淘汰策略

  • 解决方案

(1)分散缓存时间

将缓存时间分散,比如在原有的实效时间上加一个随机数,例如不同key的过期时间,可以设置为10分1秒过期,10分23秒过期。

过期时间分散,对于热点数据,过期时间尽量设置得长一点,冷门数据过期时间可以短一点,这样留足够的时间给redis进行数据持久化和主从同步。

(2)设置过期策略

缓存和数据库数据不一致

4种解决方案https://coolshell.cn/articles/17416.html

常用的是 Cache Aside Pattern

即先修改数据库,再删除缓存;在查询时,将对应的数据设置回redis。

参考

https://redis.io/docs/data-types/tutorial/


Redis数据结构
http://fuheihei.github.io/redis/data-type/
作者
Haha monster
发布于
2023年4月10日
许可协议