redis基本数据结构
String
string是最基本的key-value结构,key是唯一标识,value是具体的值,value不仅是字符串,也可以是数字(整数/浮点数),value最大容纳的数据长度是512M
- string的内部实现是int和SDS,SDS和C语言的字符串不太一样,没有使用C语言的字符串,
SDS可以保存文本数据,还可以保存二进制数据 - SDS获取字符串的长度的时间复杂度是O(1)
- redis的SDS API是安全的,拼接字符串不会照成缓冲区的溢出
常用的指令
1 | // 插入key |
批量操作1
2
3MSET key1 value1 key2 value2
MGET key1 key2
计数器,value为整数时候1
2
3
4
5
6
7
8
9
10
11
12
13SET number 0
// 增加1
INCR number
// 将key存储的数字值增加10
INCRBY number 10
// 减少1
DECR number
// 减少10
DECRBY number 10
设置过期,默认永不过期1
2
3
4
5
6
7
8
9// 设置key在60s后过期,(key已经存在)
EXPIRE name 60
// 查看数据还有多久过期
TTL name
// 设置key-value类型数据多久过期
SET key value EX 60
SETEX key 60 value
1 | SETNX key value |
使用场景
- 使用string来缓存对象
- 直接缓存整个对象json,例如命令: SET user:1’{“name”: “panic”, “age”: 18}’
- 采用key进行分离user:ID属性,使用MSET存储,使用MGET获取属性:
MSET user:1:name panic user:1:age 18, 读取:MGET user:1:name user:1:age
常规计数:由于redis本身是单线程的,所以执行过程是原子的,因此string数据类型适合计数场景,比如点赞、次数、转发、库存数量等等
例如文章阅读数量:1
2
3SET article:read:00001 0
INCR article:read:00001分布式锁
set nx实现如果key存在就插入,否则失败,可以用来做分布式唯一标识
一般是set nx ex/px 时间,
由于解锁的时候需要确保是加锁的客户端解锁,需要多一个判断,这时候使用lua脚本将操作串起来1
2
3
4
5if redis.call("get", KEY[1]) == ARGV[1] then
return redis.call("del", KEY[1])
else
return 0
end存储登录态的session信息
登陆后的信息如果存储在不同的服务器上,会导致登录信息错乱,需要有一个统一的中心管理
List
简单的存储string的列表,
底层数据结构是双向链表或者压缩链表组成的
如果元素小于512个(默认值,可由list-max-ziplist-entries设置)列表中每个元素的值都小于64字节,那么redis会使用压缩链表作为存储的数据结构,否则redis会使用双向链表
redis3.2之后,list用的是quicklist,代替了上面两个
quicklist = ziplist + 双向链表
例如存储a,b,c,d,e,f,g,h
数据格式大概是1
2
3
4
5
6
7
8
9quicklist
│
▼
+--------+ +--------+
| node1 | <-> | node2 |
+--------+ +--------+
│ │
▼ ▼
[a,b,c,d] [e,f,g,h]
可以设置ziplist最大存储数量,小于这个数量可以新增,等于时候开新的节点
为什么这么做?
早期的时候双向链表存储指针太多,内存占用太大
使用命令
1 | // 从左边插入元素,最后插入的元素在最前面 |
使用场景
- 消息队列
消息队列取消息时候必须保证消息保序、处理重复消息和保证消息可靠性
1 | ------>消费者 |
处理重复的消息,每个消息做一个全局ID,消费者要记录已经处理过的消息ID,当收到一条消息后,消费者程序可以对比收到的和已经处理过的消息,如果已经处理过,那么消费者程序就不再处理了
保证消息的可靠性: 提供了BRPOPLPUSH指令,拿到了消息后将消息备份在另外一个List中,也叫做List留存
Hash
Hash是key-value集合,value格式是:1
value = [{field1, value1}, {field2, value2}...]
Hash存储对象比string更加方便
内部实现:
压缩链表或者哈希表
使用命令
1 | HSET key field value |
使用场景
缓存对象
hash类型的(key, field, value)的结构与对象(对象ID,属性,值)的结构相似,也可以用来存储对象1
2
3
4HMSET uid:1 name Tom age 15
// 拿到uid为1的所有对象的值
HGETALL uid:1购物车
![[{3AD5584C-7DD5-4AC6-BC57-37CBFDA9D917}.png]]
Set
无序且唯一的键对集合1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17// key中加入member元素,已经存在member则忽略,不存在key的时候新建
SADD key member [member...]
// 从key里面删除元素
SREM key member [member]
// 获取所有的key
SCARD key
// 判断member是否存在于集合key中
SISMEMBER key member
// 从key中随机取出某些元素,元素不删除
SRANDEMEMBER key [count]
// 从集合key中随机选出count个元素,元素从key中删除
SPOP key [count]
SET的运算操作1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16// 交集运算
SINTER key [key ...]
// 将交集结果存入新的集合destination中
SINTERSTORE destination key [key...]
// 并集运算
SUNION key [key...]
// 将并集合存入新的destination
SUNIONSTORE destination key [key...]
// 差集
SDIFF key [key..]
// 将差集存入新的destination中
SDIFFSTORE destination key [key...]
应用
- 点赞
1
2
3
4
5// uid:1对article:1点赞
SADD article:1 uid:1
// uid:2对于article1点赞
SADD article:1 uid:2
1 | // 取消点赞 |
1 | // 判断用户uid:1是否对文章的article1点赞 |
共同关注
1
2
3
4
5SADD uid:1 5 6 7 8 9
SADD uid:2 6 7 8 99 992
// 1和2共同关注的公众号
SINTER uid:1 uid:2抽奖
1
2
3
4
5
6
7SADD lucky tom cat nanshou xiangsi
// 抽取n个人作为x等奖励
SRANDEMEMBER lucky [数字n]
// 如果不允许重复中奖,勇SPOP
SPOP lucky [数字n]
ZSET
比起set多加了一个排序的score(分值),对于Zset来说每个储存元素相当于有两个值组成,一个是有序集合的元素值,一个是排序值
底层结构使用的是跳表,
跳表的实现细节:
插入一个数字时候,使用概率算法随机生成当前插入的层数,尽量保证上面的数字少,下面的数字大,之后从对应层从后往前便利,插入这个数字使得每一层的链表保持递增,同时维护update用来存储上一个更新的forward指针位置,更新每层span使得尽可能平衡
查询一个数字时候从最上层开始,依次遍历到下一个数字比查询的数字大时候跳到下一层,直到找到了或者遍历完了
命令
1 | ZADD key score member [[score member] ...] |
应用场景
- 排行榜单
以点赞排行版为例子
每次点赞1
2SADD article:userlike user1
ZINCRBY article:userlike 1 user1
统计某个书1
ZCARD article:userlike
拿到topk1
2// 获得最大的三排文章
ZREVRANGE article:userlike 0 2
Bitmap
用的是string,byte数组来存储
1 | // 设置值 value只能是0 / 1 |
运算操作1
2
3
4
5// AND OR XOR NOT
BITOP [操作类型(AND, OR, XOR)] [result] [key1] [key2], ....
// 返回key中第一次出现value的位置
BITPOS [key] [value]
1 | // 用户100 在20260314 2号 签到 |
HyperLogLog
适合用于小内存存储大量的数据,实现比较玄学,有概率不准确,准确的概率有78%
是用于统计用户登陆的UV1
2
3
4
5// 某个key加member
PFADD key member [member1 ...]
// 统计基数
PFCOUNT key
例如1
2
3
4
5
6
7
8PFADD login user1
PFADD login user2
PFADD login user3
PFCOUNT login
// 输出3
PFMERGE destkey sourcekey [sourcekey]
GEO
地图用的
Stream
得学
