Redis核心数据结构
常用数据结构
基本数据类型
string
- 最常用的一种数据类型,占用大小最大不能超过512MB
hash
- 基于K-V的键值对集合
list
- 依赖双向链表实现的有序可重复集合
set
- 无序去重的集合,提供了交、并集等方法,对于实现共同好友、共同关注等功能特别方便
zset
- 有序set,内部维护了一个score,适用于排行榜和带权重的消息队列等
高级数据类型
-
bitmap
- 位图,可以看作一个单位数组,数组中的单元只能存0和1,数组的下标在bitmap中叫做偏移量,bitmap的长度与集合中元素个数无关,而是与基数的上限有关
-
hyperloglog
- hyperloglog是用来做基数统计的算法,优点在于输入元素的数量或者体积非常大时,计算基数所需的空间总是固定的并且很小的,典型的应用场景就是统计独立方可
-
geospatial
- 主要用于存储地理位置信息,并且存储的信息进行操作,应用场景如定位、附近的人等
-
常用指令操作
- Redis指令手册: https://redis.io/commands/set
string常用指令
-
常用结构
-
单值缓存
-
set
key value -
get
key value -
del
key … -
expire
key value -
对象缓存
set
user <json数据>mset
user:1:name linc user1:balance 888mget
user:1:name linc user:1:balance
-
分布式锁
setnx
product:101 true
-
-
计数器
incr
article:count:999
-
-
应用场景
Web集群Session共享
Spring Session + Redis实现Session共享
hash常用指令
-
常用结构
-
hset
key field value -
hmset
user userid:name linc userid:balance 999 -
hmget
userid:name userid:balance
-
-
应用场景
-
购物车
- 添加商品:
hset cart:1001 10088 1
- 增加数量:
hincrby cart:1001 10088 1
- 商品总数:
hlen cart:1001
- 删除商品:
hdel cart:1001 10088
- 获取购物车所有商品:
hgetall cart:1001
- 添加商品:
-
-
优点
- 同类数据归类整合存储,方便数据管理
- 相比string操作消耗内存与CPU更小
- 相比string操作存储更节省内存
-
缺点
- 过期功能不能使用field上,只能用在key上
- Redis集群架构下不适合大规模使用
list常用指令
-
常用结构
-
lpush
key value [value …] -
将一个或多个value插入到key列表的最左边
-
rpush
key value [value …]- 将一个或多个value插入到key列表的最右边
-
lpop
key- 移除并返回key列表的头元素
-
rpop
key- 移除并返回key列表的尾元素
-
brpop
key- 从key列表尾弹出一个元素,如果列表中没有元素,阻塞等待timeout秒.如果timeout=0,则一直阻塞
-
blpop
key- 从key列表表头弹出一个元素,如果列表中没有元素,阻塞等待timeout秒.如果timeout=0,则一直阻塞
-
lrange
key start stop- 返回列表key中指定区间内的元素,区间以偏移量start和stop指定范围
-
-
应用场景
微信公众号的消息流
- A发微博:
lpush msg:msgid1 msgid2
- B发微博:
lpush msg:msgid2 msgid1
- 查看最新微博信息:
lrange msg:msgid 0 4
- A发微博:
微博发布的新闻
队列的实现
Stack: lpush + lpop
Queue: lpush + rpop
BlockingQueue: lpush + brpop
Redis中发布/订阅
set常用指令
-
常用结构
sadd
key member [member…]srem
key member [member…]smembers
key- 获取集合key中的所有元素
scard
key- 获取集合key的元素个数
sismember
key- 判断member元素是否存在于集合key中
srandmember
key [count]- 从集合key中选出count个元素,元素不从key中删除
spop
key [count]- 从集合key中选出count个元素,元素从key中删除
-
运算结构
sinter
key [key…]- 求交集
sinterstore
destination key [key…]- 将交集结果存入新集合destination中
sunion
key [key …]- 求并集
sdiff
key [key…]- 求差集
sdiffstore
destination key [key …]- 将差集结果存入新集合destination中
-
应用场景
-
微信抽奖小程序
- 点击参与抽奖加入集合:
sadd key userid
- 查看参与抽奖的所有用户:
smembers key
- 抽取count位中奖者:
srandmember key [count]
或spop key [count]
- 点击参与抽奖加入集合:
-
微博微信点赞、收藏、关注
- 点赞:
sadd like:messageid userid
- 取消点赞:
srem like:messageid userid
- 检查用户是否点赞:
sismember like:messageid userid
- 获取点赞的用户列表:
smembers like:messageid
- 获取点赞用户数:
scard like:messageid
- 点赞:
-
集合操作,实现微博微信关注
- A关注的人:
aset
- B关注的人:
bset
- AB共同关注的人:
sinter aset bset
- 我关注的人:
sismember
- 我可能认识的人:
sdiff aset bset
- A关注的人:
-
电商平台的根据不同的条件筛选出对应的商品
- sadd brand:huawei p40
sadd brand:xiaomi mi-10
sadd brand:iphone iphone12
sadd os:android p40 mi-10
sadd cpu:brand:intel p40 mi-10
sadd ram:8g p40 mi-10 iphone12
sinter os:android cpu:brand:intel ram:8g -> {p40,mi-10}
- sadd brand:huawei p40
-
-
注意情况
- Set集合一个key不能存放太多数据,建议5k以下,如果超过5k,性能会逐渐下降,所以尽量避免大key,如果数据确实比较多,可以将数据分片存放,比如: 10w数据,将数据取模运算分别存放在20个key中,相当于每个key平均分配到5k记录,也可以分更多,分片的目的是为了避免大key,所以根据自己业务需求来处理
zset常用指令
- 常用操作
zadd
key score member [[score member]…]- 往有序集合key中加入带分值元素
zrem
key member [member …]- 从有序集合key中删除元素
zscore
key member- 返回有序集合key中元素member的分值
zincrby
key increment member- 为有序集合key中元素member的分值加上increment
zcard
key- 返回有序集合key中元素个数
zrange
key start stop [withscores]- 正序获取有序集合key从start下标到达stop下标的元素
zrevrange
key start stop [withscores]- 倒序获取有序集合key从start下标到达stop下标的元素
zrangebysocre
key min max [withscores] [limit]- 通过分数返回有序集合指定区间内的成员
zrevrangebysocre
key min max [withscores] [limit]- 返回有序集中指定分数区间内的成员,分数从高到低排序
- 运算操作
zunionstore
destkey numkeys key [key …]- 求并集
zinterstore
destkey numkeys key [key …]- 求交集
应用场景
微博排行榜
- 点击新闻:
zincrby hotnews:20220505 1 抗疫
- 展示当日排行榜前10:
zrevrange hotnews:20220505 0 9 withscores
- 七日搜索榜单计算:
zunionstore hotnews:20220505-20220512 7
- 展示七日排行榜前10:
zrevrange hotnews:20220505-20220512 0 9 withscores
- 点击新闻:
思考:
给一个包含1亿关键词的用户检索日志,如何取出排行榜前10的关键词(服务器配置: 2核2G一台)- Map + 分治算法 + 堆树
场景题
- 网站首页公告,假设
公告id是连续
,不间断的,要求使用Redis存储,并且根据评论的时间排序,而且还有分页的功能,请给出你的解决方式,从存储方式和查询方式
分析,以及分页怎么获得总数据数,分页怎么分- 存储方式: zset
- zset(公告key,评论的时间戳,公告信息)
- 查询方式
分页
- zrevrange/zrange
获取记录总数
- zcard
- 存储方式: zset
- 某个文章的评论,要求使用Redis存储,并且根据评论的时间排序,而且还有分页的功能,请给出你的解决方式,从
存储方式和查询方式
分析,以及分页怎么获得总数据数,分页怎么分- 存储方式: zset
- zset(公告key,评论的时间戳,公告信息)
- 查询方式
分页
加载第一页数据
: zrevrange/zrange加载第一页后数据
: zrevrangebysocre/zrangebysocre
获取记录总数
- zcard
- 存储方式: zset
- 如何通过Redis实现分布式锁
- 分布式锁需要解决的问题
- 互斥性: 只能有一个客户端持有锁
- 安全性: 锁只能由持有锁的客户端释放
- 容错: 如果持有锁的客户端宕机,也应该释放锁
- 死锁
- 可以通过
setnx key value
,但是这个有个弊端就是如果持有锁的客户端宕机,锁就无法过期
,那就得想个办法设置一个过期时间,同时让setnx与设置过期时间是一个原子操作,所以可以使用set key value ex seconds nx
- 同样的有个setex指令,这个指令每次都返回ok与setnx相比,缺少判断是否获取到锁
- 最终实现分布式锁采用
set key value ex seconds nx
- 分布式锁需要解决的问题
- 如何使用zset实现延迟队列,定期执行任务
- 可以将zset的
score存放任务的到期时间
,利用zset默认有序特性,通过zrange可以获取值最小的元素(也就是最近到期的任务),判断系统时间与该任务的到期时间大小,如果到达到期时间就执行业务,并删除该到期任务,继续判断下一个元素,如果没有到期,就sleep一段时间,如果集合为空,也sleep一段时间
- 可以将zset的
- 如何实现zset的元素过期删除
- 可以将zset的
score存放元素的到期时间
,然后通过定时任务每隔一段时间去用zrange获取最近到期的元素然后与当前时间进行比较,如果小于当前时间表示已过期需要删除,反之没过期
- 可以将zset的