文章

Redis 核心数据结构

核心数据结构

查询用法:如 help string

string 字符串

常用命令

# 基本操作
SET {key} {value}  # 设置一个键值对
MSET {key1} {value1} {key2} {value2} {key3} {value3} …  # 批量设置键值对
SETNX {key} {value}  # 设置一个不存在的键值对(键不存在才能设置成功)
GET {key} {value}  # 获取一个键值
MGET {key1} {key2} {key3} …  # 批量获取键值 
DEL {key} {value}  # 删除一个键
EXPIRE {key} {seconds}  # 设置一个键的过期时间

# 原子加减
INCR {key}  # 将键中存储的数字值加1
DECR {key}  # 将键中存储的数字值减1
INCRBY {key} {num}  # 将键中存储的数字值加上num
DECRBY {key} {num}  # 将键中存储的数字值减去num

使用场景

(1) 单值缓存

以热更新状态为例:

SET hotupdate:enable true  # 设置热更新开启
GET hotupdate:enable  # 查询热更新状态

(2) 对象缓存

以 user 为例:

# value是JSON字符串格式,适合很少修改的数据
SET user:1 {value(JSON字符串格式)}

# 将 value 分属性一一存储,适合经常修改的数据,可以方便修改部分属性
MSET user:1:name Tom user:1:balance 100
MGET user:1:name user:1:balance

(3) 分布式锁

以操作id为1001的商品为例:

SET product:1001 true ex 10 nx  # 返回1表示操作获取锁成功,返回0表示获取锁失败(需要设置超时时间,避免程序意外终止导致的死锁)

#  获取到锁的线程执行业务操作…
DEL product:1001  # 执行完业务操作释放锁

(4) 计数器

以id为666的文章阅读量为例:

INCR article:readcount:666
GET article:readcount:666

```

(5) Web集群Session共享

如:集群间 Spring Session 共享

(6) 分布式系统全局id

INCR orderId 1000  # 一次分配一个批id(如1000个)给一个客户端节点,该节点用光这一批id再来redis申请下一批,这样性能更高

hash 哈希

hash相当于双重map结构

优点:

  1. 整合了同类数据,方便数据管理

  2. 相比string操作时更节省内存和cpu

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

缺点:

  1. 不支持在filed上设置过期,只能在key上设置过期

  2. Redis集群架构下不适合大规模使用

常用命令

HSET {key} {filed} {value}  # 设置某个hash的一个filed->value对
HSETNX {key} {filed} {value}  # 设置某个hash的一个不存在的filed->value对
HMSET {key} {filed1} {value1} {filed2} {value2} {filed3} {value3} …  # 设置某个hash的多个filed->value对
HGET {key} {filed}  # 获取某个hash的某个filed对应的值
HGET {key} {filed1} {filed2} {filed3} …  # 获取某个hash的多个filed对应的值
HDEL {key} {filed}  # 删除某个hash的某个filed->value对
HLEN {key}  # 获取某个hash的所有filed数目
HGETALL {key}  # 获取某个hash的所有filed->value对

使用场景

(1) 对象缓存(注意bigkey问题,如果key对应的filed->value对太多,则操作该key的性能很低,需要进行分段处理)

HMSET user 1:name Tom 1:balance 100
HMSET user 1:name 1:balance

(2) 购物车(最终购物车数据还是要入库)

以用户id为key,商品id为field,商品数量为value:

HSET cart:1 1001 1  # id为1的用户添加1个id为1001的商品到购物车
HINCRBY cart:1 1001 1  # id为1的用户将购物车中id为1001的商品的数量加1
HLEN cart:1  # 查询id为1的用户的购物车商品数目
HDEL cart:1 1001  # 将id为1的用户的购物车中id为1001的商品删除
HGETALL cart:1  # 获取id为1的用户的购物车中的所有商品

list 列表

常用命令

LPUSH {key} {value1} {value2} {value3} …  # 将一个或多个value值插入到列表key的头部(最左边)
RPUSH {key} {value1} {value2} {value3} …  # 将一个或多个value值插入到列表key的尾部(最右边)
LPOP {key}  # 移除并返回列表key的头元素
RPOP {key}  # 移除并返回列表key的尾元素
BLPOP {key} {timeout}  # 移除并返回列表key的头元素,如列表中没有元素,阻塞等待timeout秒(timeout为0时会一直阻塞等待)
BRPOP {key} { {timeout}}  # 移除并返回列表key的尾元素,如列表中没有元素,阻塞等待timeout秒(timeout为0时会一直阻塞等待)
LRANGE {key} {start} {stop}  # 返回列表key指定区间内的元素,start和stop分别表示区间起始偏移量

list实现分布式数据结构

  1. 栈(Stack):LPUSH + LPOP

  2. 队列(Queue):LPUSH + RPOP

  3. 阻塞队列(Blocking Queue):LPUSH + BRPOP

使用场景:按时间排序的消息流

以微信公众号为例,假设id为1的用户关注了微信公众号A、B、C,id为2的用户关注了微信公众号A、C:

# 1.公众号A发布文章,文章id为1001
LPUSH subscription:1 1001
LPUSH subscription:2 1001

# 2.公众号B发布文章,文章id为1234
LPUSH subscription:1 1234

# 3.公众号C发布文章,文章id为2333
LPUSH subscription:1 2333
LPUSH subscription:2 2333

# 4.id为1的用户查看微信订阅中最新的10篇文章
LRANGE subscription:1 0 9

# 5.id为2的用户查看微信订阅中最新的10篇文章
LRANGE subscription:2 0 9

如果公众号有很多订阅用户,需要做相应的优化,如一开始只给在线的用户发,其他用户在后台慢慢发;如使用PULL模式,或PUSH和PULL相结合

set 集合

常用命令

# 基本操作
SADD {key} {member1} {member2} {member3} …  # 往集合key中添加元素,元素存在则忽略,key不存在则创建
SREM {key}{member1} {member2} {member3} …  # 从集合key中移除元素
SMEMBERS {key}  # 获取集合key中的所有元素
SCARD {key}  # 获取集合key中的元素个数
SISMEMBER {key} {member}  # 判断元素member是否存在于集合key中
SRANDMEMBER {key} {count}  # 随机从集合key中选出count个元素,不删除选出的元素
SPOP {key} {count}  # 随机从集合key中选出count个元素,并删除选出的元素

# 集合操作
SINTER {key1} {key2} {key3} …  # 交集运算
SINTERSTORE {destination} {key1} {key2} {key3} …  # 将交集运算的结果放入新集合destination中
SUNION {key1} {key2} {key3} …  # 并集运算
SUNIONSTORE {destination} {key1} {key2} {key3} …  # 将并集运算的结果放入新集合destination中
SDIFF {key1} {key2} {key3} …  # 差集运算(在集合key1中,不在集合key2,key3…中)
SDIFFSTORE {destination} {key1} {key2} {key3} …  # 将差集运算的结果放入新集合destination中

使用场景

(1) 抽奖

# id为1的用户参与id为18183的抽奖活动
SADD choujiang:18183 1
# 查看参与抽奖的所有用户
SMEMBERS choujiang:18183
# 开奖方式1:抽2个人中奖
SRANDMEMBER choujiang:18183 2
# 开奖方式2:5个三等奖,3个二等奖,1个一等奖
SPOP choujiang:18183 5
SPOP choujiang:18183 3
SPOP choujiang:18183 1

(2) 点赞 & 收藏

# 点赞
SADD like:{文章id} {用户id} 
# 取消点赞
SREM like:{文章id} {用户id} 
# 查询用户是否点赞
SISMEMBER like:{文章id} {用户id} 
# 查询所有点赞的用户
SMEMBERS like:{文章id}
# 查询点赞数
SCARD like:{文章id}

(3) 关注模型(通过集合操作实现)

# Tom关注的人
SMEMBERS followee:Tom -> {Jerry, Spike, Topsy}
# Jerry关注的人
SMEMBERS followee:Jerry -> {Tom, Spike, Tuffy}
# Tom和Jerry共同关注
SINTER followee:Tom followee:Jerry -> {Spike}
# Tom可能认识的人
SDIFF followee:Jerry followee:Tom -> {Tuffy}

(4) 多级筛选(通过差集运算实现)

SINTER os:Android cpu:HiSilicon ram:16G -> {P40}

zset 有序集合

常用命令

# 基本操作
ZADD {key} {score1} {member1} {score2} {member2} {score3} {member3} …  # 往有序集合key中添加带分值的元素
ZREM {key} {member1} {member2} {member3} …  # 从有序集合key中删除元素
ZSCORE {key} {member}  # 查询有序集合key中元素member的分值
ZINCRBY {key} {increment} {member}   # 将有序集合key中元素member的分值增加increment
ZCARD {key}  # 查询有序集合key的元素个数
ZRANGE {key} {start} {stop} [WITHSCORES]  # 按照分值从低到高的顺序,获取有序集合key中指定区间的元素
ZREVRANGE {key} {start} {stop} [WITHSCORES]  # 按照分值从高到低的顺序,获取有序集合key中指定区间的元素

# 集合操作
ZINTERSTORE {destkey} {numkeys} {key1} {key2} {key3} …  # 交集运算
ZUNIONSTORE {destkey} {numkeys} {key1} {key2} {key3} …  # 并集运算

使用场景:排行榜

以每日热点新闻为例:

# 点击新闻
ZINCRBY hotNews:20240523 1 {}
# 展示当天的10条热点新闻
ZREVRANGE hotNews:20240523 0 9 WITHSCORES
# 计算一周的10条热点新闻
ZUNIONSTORE hotNews:20240517-20240523 7 hotNews:20240517 hotNews:20240518 … hotNews:20240523
# 展示一周的10条热点新闻
ZREVRANGE hotNews:20240517-20240523 0 9 WITHSCORES

License:  CC BY 4.0