Redis 的五种数据结构及其应用场景
Redis官方文档链接:https://redis.io/commands 中文文档:http://redisdoc.com/
一、字符串
命令 | 解释 |
---|---|
SET key value | 存入字符串键值对 |
MSET key value [key value …] | 批量存储字符串键值对 |
SETNX key value | 存入一个不存在的字符串键值对 |
GET key | 获取一个字符串键值 |
MGET key [key …] | 批量获取字符串键值 |
DEL key [key …] | 删除一个键 |
EXPIRE key seconds | 设置一个键的过期时间(秒) |
INCR key | 将key中储存的数字值加1 |
DECR key | 将key中储存的数字值减1 |
INCRBY key increment | 将key所储存的值加上increment |
DECRBY key decrement | 将key所储存的值减去decrement |
应用场景
-
单值缓存
SET key value GET key
-
对象存储
直接存储对象
SET user:1 "{"name":"zhangsan","age":18}" GET user:1
拆分对象存储
MSET user:1:name zhangsan user:1:age 18 MGET user:1:name user:1:age
-
分布式锁
加锁 SETNX product:10001 true // 第一次执行,返回1代表获取锁成功 SETNX product:10001 true // 再次执行,返回0代表获取锁失败 删锁 DEL product:10001 // 执行完业务释放锁,防止过期不存在可以使用lua脚本 加锁时设置超时时间 SET product:10001 true ex 10 nx // 防止程序意外终止导致死锁
-
分布式系统全局流水
INCRBY id 2000 // redis 批量生成序列号提升性能
-
计数器
例如微信推文的阅读量、视频播放量等 INCR article:readcount:{文章id} GET article:readcount:{文章id}
-
Web集群 session 共享
spring session 提供了通过 Redis 设置 session 共享的功能
二、Hash
命令 | 解释 |
---|---|
HSET key field value | 存储一个哈希表key的键值 |
HSETNX key field value | 存储一个不存在的哈希表key的键值 |
HMSET key field value [field value …] | 在一个哈希表key中存储多个键值对 |
HGET key field | 获取哈希表key对应的field键值 |
HMGET key field [field …] | 批量获取哈希表key中多个field键值 |
HDEL key field [field …] | 删除哈希表key中的field键值 |
HLEN key | 返回哈希表key中field的数量 |
HGETALL key | 返回哈希表key中所有的键值 |
HINCRBY key field increment | 为哈希表key中field键的值加上增量increment |
应用场景
-
对象缓存
HMSET user {userId}:name zhangsan {userId}:age 18 HMSET user 1:name zhuge 1:age 18 HMGET user 1:name 1:age
-
商品购物车
Hash类型的优缺点
优点:
-
同类数据归类整合储存,方便数据管理
-
相比 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列表的尾元素 |
LRANGE key start stop | 返回列表key中指定区间内的元素,区间以偏移量start和stop指定 |
BLPOP key [key …] timeout | 从key列表表头弹出一个元素,若列表中没有元素,阻塞等待timeout秒,如果timeout=0,一直阻塞等待 |
BRPOP key [key …] timeout | 从key列表表尾部弹出一个元素,若列表中没有元素,阻塞等待timeout秒,如果timeout=0,一直阻塞等待 |
通过这些操作,可以组成一些常用的数据结构,应用到不同的场景中
- Stack(栈) = LPUSH + LPOP
- Queue(队列)= LPUSH + RPOP
- Blocking MQ(阻塞队列)= LPUSH + BRPOP
应用场景
微博消息、微信订阅号等消息流
四、Set
常用基本操作 | 解释 |
---|---|
SADD key member [member …] | 往集合key中存入元素,元素存在则忽略,若key不存在则新建 |
SREM key member [member …] | 从集合key中删除元素 |
SMEMBERS key | 获取集合key中所有元素 |
SCARD key | 获取集合key的元素个数 |
SISMEMBER key member | 判断member元素是否存在于集合key中 |
SRANDMEMBER key [count] | 从集合key中选出count个元素,元素不从key中删除 |
SPOP key [count] | 从集合key中选出count个元素,元素从key中删除 |
Set集合的运算操作 | 解释 |
---|---|
SINTER key [key …] | 交集运算 |
SINTERSTORE destination key [key …] | 将交集结果存入新集合destination中 |
SUNION key [key …] | 并集运算 |
SUNIONSTORE destination key [key …] | 将并集结果存入新集合destination中 |
SDIFF key [key …] | 差集运算 |
SDIFFSTORE destination key [key …] | 将差集结果存入新集合destination中 |
应用场景
- 微信、支付宝抽奖小程序
- 微博、微信等点赞、收藏、标签
-
微信、微博、QQ关注模型、社交模型
1) 我关注的人: meSet--> {特朗普, 拜登 ,汤姆逊} 2) 特朗普关注的人: telangpuSet --> {普京, 拜登, 安倍晋三, 汤姆逊} 3) 拜登关注的人: pujingSet--> {特朗普, 普京, 汤姆逊, 安倍晋三, 拿破仑) 4) 我和特朗普共同关注: SINTER meSet telangpuSet--> {汤姆逊, 拜登} 5) 我关注的人也关注他(杨过老师): SISMEMBER telangpuSet 普京 SISMEMBER pujingSet 普京 6) 我可能认识的人: SDIFF telangpuSet meSet --> (安倍晋三, 普京}
-
商城商品筛选
为品牌下插入商品
SADD brand:huawei Huawei-P50
SADD brand:vivo vivo-x70
SADD brand:xiaomi Mi-12
SADD brand:iPhone iPhone13
为操作系统下插入商品
SADD os:android vivo-x70 Mi-12
SADD os:ios iPhone13
SADD os:harmony Huawei-P50
为CPU、内存等插入商品
SADD cpu:brand:qualcomm Huawei-P50 Mi-12 vivo-x70
SADD ram:8G Huawei-P50 vivo-x70 Mi-12 iPhone13
取交集获取商品搜索需要的标签
SINTER os:android cpu:brand:qualcomm ram:8G -> {Mi-12 vivo-x70}
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下标的元素 |
ZSet集合运算 | 解释 |
---|---|
ZUNIONSTORE destkey numkeys key [key …] | 并集运算,并将结果存入destkey |
ZINTERSTORE destkey numkeys key [key …] | 交集运算,并将结果存入destkey |
应用场景
排行榜–微博热搜、热搜周榜、同城榜等
1)点击新闻
ZINCRBY hotNews:20220218 1 xxxx
2)展示当日排行前十
ZREVRANGE hotNews:20220218 0 9 WITHSCORES
3)七日搜索榜单计算
ZUNIONSTORE hotNews:20220212-20220218 7
hotNews:20220212 hotNews:20220213... hotNews:20220218
4)展示七日排行前十
ZREVRANGE hotNews:20220212-20220218 0 9 WITHSCORES-
Redis高性能原理
-
Redis是真的单线程吗?
Redis 的单线程主要是指 Redis 的**网络 IO 和键值对读写是由一个线程来完成的,这也是 Redis 对外提供键值存储服务的主要流程。但 Redis 的其他功能,比如持久化、异步删除、集群数据同步等是由额外的线程执行的**。
-
Redis 单线程为什么这么快?
所有的操作都是在内存中完成的,纯内存操作非常快,单线程也避免了多线程上下文的切换对性能的损耗。但是因为Redis是单线程,所有必须谨慎使用耗时的操作(例如:keys)。
-
Redis 单线程如何处理并发客户端连接?
Redis采用了IO的多路复用机制,redis 采用 epoll 实现了 IO 多路复用,将连接信息和事件放到队列中,依次放到文件事件分派器,事件分派器将事件分发给事件处理器。
其他命令及注意事项
- keys [MATCH pattern]:遍历全键,找出所有满足特定正则字符串规则的 key,应避免使用,当数据量大时,及其销毁性能
- scan cursor [MATCH pattern] [COUNT count] :渐进式遍历键,第一个是 cursor 整数值(hash桶的索引值),第二个是 key 的正则模式, 第三个是一次遍历的key的数量(参考值,底层遍历的数量不一定),并不是符合条件的结果数量。第一次遍历时,cursor 值为 0,然后将返回结果中第一个整数值作为下一次遍历的 cursor。一直遍历到返回的 cursor 值为 0 时结束。在 scan 过程中,如果有键的变化(增加、 删除、 修改),那么可能出现遍历重复、遗漏等现象
- Info:查看redis服务运行信息,分为 9 大块,每个块都有非常多的参数
-
- Server 服务器运行的环境参数
- Clients 客户端相关信息
- Memory 服务器运行内存统计数据
- Persistence 持久化信息
- Stats 通用统计数据
- Replication 主从复制相关信息
- CPU CPU 使用情况
- Cluster 集群信息
- KeySpace 键值对统计数量信息
Redis客户端命令对应的RedisTemplate中的方法列表:
String类型结构 | |
---|---|
Redis | RedisTemplate rt |
set key value | rt.opsForValue().set(“key”,“value”) |
get key | rt.opsForValue().get(“key”) |
del key | rt.delete(“key”) |
strlen key | rt.opsForValue().size(“key”) |
getset key value | rt.opsForValue().getAndSet(“key”,“value”) |
getrange key start end | rt.opsForValue().get(“key”,start,end) |
append key value | rt.opsForValue().append(“key”,“value”) |
Hash结构 | |
hmset key field1 value1 field2 value2… | rt.opsForHash().putAll(“key”,map) |
hset key field value | rt.opsForHash().put(“key”,“field”,“value”) |
hexists key field | rt.opsForHash().hasKey(“key”,“field”) |
hgetall key | rt.opsForHash().entries(“key”) |
hvals key | rt.opsForHash().values(“key”) |
hkeys key | rt.opsForHash().keys(“key”) |
hmget key field1 field2… | rt.opsForHash().multiGet(“key”,keyList) |
hsetnx key field value | rt.opsForHash().putIfAbsent(“key”,“field”,“value” |
hdel key field1 field2 | rt.opsForHash().delete(“key”,“field1”,“field2”) |
hget key field | rt.opsForHash().get(“key”,“field”) |
List结构 | |
lpush list node1 node2 node3… | rt.opsForList().leftPush(“list”,“node”) |
rt.opsForList().leftPushAll(“list”,list) | |
rpush list node1 node2 node3… | rt.opsForList().rightPush(“list”,“node”) |
rt.opsForList().rightPushAll(“list”,list) | |
lindex key index | rt.opsForList().index(“list”, index) |
llen key | rt.opsForList().size(“key”) |
lpop key | rt.opsForList().leftPop(“key”) |
rpop key | rt.opsForList().rightPop(“key”) |
lpushx list node | rt.opsForList().leftPushIfPresent(“list”,“node”) |
rpushx list node | rt.opsForList().rightPushIfPresent(“list”,“node”) |
lrange list start end | rt.opsForList().range(“list”,start,end) |
lrem list count value | rt.opsForList().remove(“list”,count,“value”) |
lset key index value | rt.opsForList().set(“list”,index,“value”) |
Set结构 | |
sadd key member1 member2… | rt.boundSetOps(“key”).add(“member1”,“member2”,…) |
rt.opsForSet().add(“key”, set) | |
scard key | rt.opsForSet().size(“key”) |
sidff key1 key2 | rt.opsForSet().difference(“key1”,“key2”) |
sinter key1 key2 | rt.opsForSet().intersect(“key1”,“key2”) |
sunion key1 key2 | rt.opsForSet().union(“key1”,“key2”) |
sdiffstore des key1 key2 | rt.opsForSet().differenceAndStore(“key1”,“key2”,“des”) |
sinter des key1 key2 | rt.opsForSet().intersectAndStore(“key1”,“key2”,“des”) |
sunionstore des key1 key2 | rt.opsForSet().unionAndStore(“key1”,“key2”,“des”) |
sismember key member | rt.opsForSet().isMember(“key”,“member”) |
smembers key | rt.opsForSet().members(“key”) |
spop key | rt.opsForSet().pop(“key”) |
srandmember key count | rt.opsForSet().randomMember(“key”,count) |
srem key member1 member2… | rt.opsForSet().remove(“key”,“member1”,“member2”,…) |