Redis之核心数据结构浅析

1、前置说明

1、命令不区分大小写

2、key区分大小写

2、string

2.1、简单示例

单值

shell 复制代码
127.0.0.1:6379> set wang 666  
OK
127.0.0.1:6379> get wang
"666"
127.0.0.1:6379> getset wang 888 # 先get,然后set
"666"
127.0.0.1:6379> get wang
"888"

多值

shell 复制代码
127.0.0.1:6379> mset k1 v1 k2 v2
OK

127.0.0.1:6379> keys * # 查看当前有多少个key
1) "k1"
2) "k2"
3) "wang"

127.0.0.1:6379> mget k1 k2 wang # 批量get
1) "v1"
2) "v2"
3) "666"

字符串拼接

shell 复制代码
127.0.0.1:6379> append wang 888
(integer) 6
127.0.0.1:6379> get wang
"666888"

数字类型(应用场景:可以做些分布式id等计数或者累计量)

shell 复制代码
127.0.0.1:6379> set count 1
OK
127.0.0.1:6379> incr count  # 仅增加1,另外使用incrby 可以指定增加多少,
(integer) 2
127.0.0.1:6379> get count
"2"

2.2、对象缓存

shell 复制代码
127.0.0.1:6379> set user:1 '"name":"wang", "money": 9999'
OK
127.0.0.1:6379> get user:1
"\"name\":\"wang\", \"money\": 9999"

shell 复制代码
127.0.0.1:6379> mset user:2:name wang user:2:money 9999  # 这是原子性的
OK
127.0.0.1:6379> mget user:2:name user:2:money
1) "wang"
2) "9999"

2.3、分布式锁

setnx 当key不存在时才会设置成功。

shell 复制代码
127.0.0.1:6379> setnx goods:1 true # 加锁
(integer) 1
127.0.0.1:6379> setnx goods:1 true # 第二次开始,就设置不成功(相当于加锁失败),返回0
(integer) 0
127.0.0.1:6379> setnx goods:1 true
(integer) 0

127.0.0.1:6379> del goods:1 # 释放锁
(integer) 1

一般防止字符串一直没被删除,导致死锁,可以设置锁持有过期时间。(实际就是字符串过期时间)

查看一个字符串的过期时间

shell 复制代码
127.0.0.1:6379> ttl k1 # 没有过期时间
(integer) -1
127.0.0.1:6379> expire k1 8 # 设置8s过期
(integer) 1
127.0.0.1:6379> ttl k1 # 再次查看过期时间
(integer) 4
127.0.0.1:6379> ttl k1
(integer) 2
127.0.0.1:6379> ttl k1 # 过期,被删除掉了
(integer) -2

完整示例

shell 复制代码
127.0.0.1:6379> set goods:1 true ex 10 # 在set时,直接设置过期时间
OK
127.0.0.1:6379> ttl goods:1
(integer) 5
127.0.0.1:6379> ttl goods:1
(integer) 4

2.4、原理

string类型本质上是一个二进制数组

shell 复制代码
127.0.0.1:6379> set k1 hh
OK
127.0.0.1:6379> getbit k1 1 # 获取第一位
(integer) 1

因此可以存储一些超文本数据,例如图片。比如logo

3、hash

3.1、结构

key <filed1, value1> <filed2, value2> <filed2, value2>

相当于一个key下维护了一个hashtable表。

3.2、简单命令示例

shell 复制代码
127.0.0.1:6379> hset k1 f1 v1 f2 v2 
(integer) 2
127.0.0.1:6379> hget k1 f1
"v1"
127.0.0.1:6379> hmget k1 f1 f2 # 同时获取多个,跟string类型,它也有hsetnx
1) "v1"
2) "v2"
127.0.0.1:6379> hgetall k1
1) "f1"
2) "v1"
3) "f2"
4) "v2"

3.3、对象缓存

shell 复制代码
127.0.0.1:6379> hset user:1 name wang money 9999 # 按照用户平开
(integer) 2
127.0.0.1:6379> hgetall user:1
1) "name"
2) "wang"
3) "money"
4) "9999"

推荐上面方式。

下面这种方式不推荐(因为有大key问题,这个key下面的数据太多了,查询慢)

shell 复制代码
127.0.0.1:6379> hset user 1:name wang 1:money 999 # 所有用户都存在user的key下
(integer) 2
127.0.0.1:6379> hgetall user
1) "1:name"
2) "wang"
3) "1:money"
4) "999"

3.4、应用场景

电商购物车(购物车本身需要快且持久化需求不高)

1、以用户id为key

2、以商品id为field

3、以商品数量为value

电商购物车操作:

添加商品:hset cart:1001 10086 1

增加商品数量:hincrby cart:1001 1006 8

商品总数:hlen cart:1001

删除商品:hdel cart:1001 10086

获取购物车所有商品:hgetall cart:1001

3.6、与string比对

hash优点

1、同类数据归类整合,方便管理。

2、相比string同类操作,消耗内存和cpu更小

3、相比string存储更节省空间

hash缺点

1、过期功能是作用在key上的,因此不能以field为单位设置过期时间。

2、Redis集群模式下,不适合大规模使用。(1、大key问题 2、集群中的数据分区时,容易造成数据的不均衡)

4、list

准确说是双端队列

4.1、简单操作示例

shell 复制代码
127.0.0.1:6379> lpush l 1 # 左侧放 对应有rpush
(integer) 1
127.0.0.1:6379> lpush l 2
(integer) 2
127.0.0.1:6379> rpush l 3 # 右侧放
(integer) 3
127.0.0.1:6379> lrange l 0 2 # 遍历,全部遍历,可以lrange l 0 -1
1) "2"
2) "1"
3) "3"

127.0.0.1:6379> lpop l 1 # 对应有rpop
1) "2"

4.2、应用场景

1、阻塞队列:lpush + brpop

2、简化版的MQ

3、视频列表、签到列表

4.3、注意事项

1、容量很大,40多亿,但是要注意大key问题。

2、本质上上双端链表,2边操作很快,但是中间操作很慢。

5、set

跟Java的数据结构差不多,其中没有重复的元素。 并且支持各种集合运算,例如交集,差集。

5.1、简单示例

shell 复制代码
127.0.0.1:6379> sadd set1 a b c d a
(integer) 4
127.0.0.1:6379> smembers set1 # 查看集合元素
1) "c"
2) "b"
3) "a"
4) "d"

5.2、应用场景

抽奖场景

1、点击参与抽奖

sadd key userid

2、查看抽奖的所有用户

smembers key

3、抽取count名中奖者

srandmember key count / spop key count

用户点赞场景

1、点赞

sadd key userid

2、取消点赞

srem key userid

3、检查用户是否点赞过

sismember key userid

朋友圈共同关注场景

可以计算交集

标签

6、zset

有序集合(Sorted Set)

即在set上面加了一个排名的功能
在set的基础上给每个值加上了一个score字段,用于排序的依据。

6.1、简单示例

shell 复制代码
127.0.0.1:6379> sadd class xm 60
(integer) 2
127.0.0.1:6379> sadd class xw 100 xh 40
(integer) 4
127.0.0.1:6379> zadd k9 30 a 20 b 60 c
(integer) 3
127.0.0.1:6379> zrange k9 0 -1 # 按照score排序的结果
1) "b"
2) "a"
127.0.0.1:6379> zrange k9 0 -1 withscores
1) "b"
2) "20"
3) "a"
4) "30"
5) "c"

127.0.0.1:6379> zincrby k9 10 a # 添加分数
"40"
127.0.0.1:6379> zscore k9 a
"40"
127.0.0.1:6379> zcount k9 30 60 # 查找30到60的个数
(integer) 2

127.0.0.1:6379> zrank k9 c # 查看c排名,从0开始
(integer) 2

6.2、应用场景

微博点击率排名

1、点击"xx"

zincrby hot:news:20250905 1 "xx"

2、展示当日排名前10

zrevrange hot:news:20250905 0 9 withscores

3、七日搜索榜单计算

zunionstore hot:news:20250905-20250921 7

4、展示七日排行前10:

zrevrange hot:news:20250905-20250921 0 9 withscores

7、bitmap

用二进制位表示的bit数组。

7.1、简单示例

shell 复制代码
127.0.0.1:6379> setbit b 1 1 # 第1位置1
(integer) 0
127.0.0.1:6379> getbit b 1
(integer) 1
127.0.0.1:6379> getbit b 0
(integer) 0
127.0.0.1:6379> bitcount b 0 -1 # 返回二进制数组中1的个数, 这里需要注意位置偏移量的默认单位是byte,可以指定bit
(integer) 1
127.0.0.1:6379> bitpos b 1 0 -1 # 二进制数组中第一个1的位置. 
(integer) 1

7.2、应用场景

签到

1、第一百天签到

setbit user:1 100 1

2、统计签到次数

bitcount user:1

3、统计第一天签到的时间

bitpos user:1 1

布隆过滤器

不在的一定不在。比如过滤登录时,不存在的用户。

优点

快速高效,节省空间

8、hyperloglog

用于统计一个集合中不重复的元素个数(算法比set更优,更适合大规模去重) - 有错误率,但不超过0.8%,12KB就可以存海量数据

典型应用场景:根据用户访问记录统计网站的UV(独立的访客)

添加用户访问记录

pfadd visitlog 1.1.1.1 1.1.1.2 1.1.1.1

pfconut visitlog // 统计不同的独立访客

另外

pfmerge destkey k1 k2 k3 // 将k1 k2 k3的hyperloglog整合到一个hyperloglog中

9、geo

基于地理空间的计算类型

入参经纬度

可以计算2地距离、查找最近距离、当前地点周围距离有啥(例如10KM内住宿)。

10、stream

Redis版本的MQ。 但是有其它专业的可以替代,比如kafka、rabbitmq。

生产几乎不使用redis的stream作为mq。

相关推荐
失散133 小时前
分布式专题——1.1 Redis单机、主从、哨兵、集群部署
java·数据库·redis·分布式·架构
DashingGuy4 小时前
算法(keep learning)
java·数据结构·算法
田里的水稻4 小时前
C++_数据类型和数据结构
java·数据结构·c++
兔兔西4 小时前
【数据结构、java学习】数组(Array)
java·数据结构·算法
小徐不徐说4 小时前
数据结构基础之队列:数组/链表
c语言·数据结构·算法·链表·面试
考虑考虑5 小时前
Redis8中的布谷鸟过滤器
redis·后端·程序员
小江村儿的文杰5 小时前
理解UE4中C++17的...符号及enable_if_t的用法及SFINAE思想
数据结构·c++·ue4
快去睡觉~8 小时前
力扣416:分割等和子集
数据结构·c++·算法·leetcode·职场和发展·动态规划
Imxyk10 小时前
力扣:2322. 从树中删除边的最小分数
数据结构·算法·leetcode