Redis常用数据结构使用场景说明和命令示例(非常实用)

Redis常用的数据结构以及各种数据结构使用场景示例(非常实用)


1、String数据结构

SET key value
// 存入字符串键值对

MSET key value [key value ...]
// 批量存储字符串键值对

SETNX key value
// 存入一个不存在的字符串键值对

GET key
// 获取字符串键值

MGET key [key ...]
// 批量获取字符串键值

DEL key [key ...]
// 删除一个键

EXPIRE key seconds
// 设置一个key的过期时间(秒)

原子性加减命令:
INCR key
// 将key中储存的数字值加1

DECR key
// 将key中储存的数字值减1

INCRBY key increment
// 将key中值加上increment

DECRBY key increment
// 将key中值减去increment

--增
set mykey "test"       --为键设置新值,并覆盖原有值
setex mykey 10 "hello" -- 设置指定 Key 的过期时间为10秒,在存活时间可以获取value
mset key3 "stephen" key4 "liu" --批量设置键
--删
del mykey --删除已有键
--改
incr mykey      --值增加1,若该key不存在,创建key,初始值设为0,增加后结果为1
decrby mykey 5  --值减少5
--查 
exists mykey   --判断该键是否存在,存在返回 1,否则返回0
get mykey      --获取Key对应的value
mget key3 key4 --批量获取键

使用场景:
1、key/value单值缓存

SET key value
GET key
商品库存 key商品ID value商品的实际库存

2、对象缓存

SET user:1 value(JSON格式数据)

3、分布式锁

命令 SETNX key value
当且仅当key不存在的时候执行操作,否则不执行任何操作

SETNX produce:10001 true
// 返回1代表获取锁成功
// 返回0代表获取锁失败
。。。执行业务操作。。。
DEL product:10001
// 执行完业务释放锁

SET product:10001 true ex 10 nx
// 防止程序意外终止导致死锁

4、原子计数器

比如博客,文章有个阅读数
只要文章被打开,我即可执行一条
INCR article:readCount:{文章ID}命令

INCR article:readCount:{文章ID}
// 每次执行一次key+1

GET article:readCount:{文章ID}
// 获取当前key的值

5、分布式系统全局序列号

INCRBY orderId 100
// redis批量生成序列号,提升性能

问题:频繁执行Redis命令会增加Redis机器的性能压力,这个时候可以考虑使用批量生成,然后在服务内存里面缓存起来使用

2、Hash数据结构

Hash的常用操作:

HSET key field value
// 存储一个哈希表key的键值

HSETNX key field value
// 存储一个不存在的哈希表的key键值

HMSET key field value [field value ...]
// 在一个Hash表中存储多个键值对

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

hset cart:{用户id} {商品id} 1  # 添加商品

hincrby cart:{用户id} {商品id} 1 # 增加数量

hlen cart:{用户id} # 获取商品总数

hdel cart:{用户id} {商品id} # 删除商品

hgetall cart:{用户id} #获取购物车所有商品

--增
hset key field1 "s"   --若字段field1不存在,创建该键及与其关联的Hash, Hash中,key为field1 ,并设value为s ,若字段field1存在,则覆盖 
hmset key field1 "hello" field2 "world" -- 一次性设置多个字段    
--删
hdel key field1 --删除 key 键中字段名为 field1 的字段
del key  -- 删除键    
--改 
hincrby key field 1 --给field的值加1
--查
hget key field1 --获取键值为 key,字段为 field1 的值
hlen key        --获取key键的字段数量
hmget key field1 field2 field3 --一次性获取多个字段
hgetall key --返回 key 键的所有field值及value值
hkeys key   --获取key 键中所有字段的field值
hvals key   --获取 key 键中所有字段的value值

Hash应用场景:
①对象缓存存储命令:

HMSET user {userId}:name 张三 {userId}:balance 18

存储数据库对象数据,使用示例如下:
HMSET user 1:name 赵思 1:balance 180
HMSET user 2:name 王五 2:balance 190
问题:
hash最重要存储对象数据,当某个值频繁被修改时,非常方便。比如用户积分,只需要操作积分的file数据即可,对象其他数据值不会受到影响

②电商购物车

1、以用户唯一ID为哈希表的key
2、以购买商品的唯一ID为field
3、商品购买数量为value

购物车操作:HSET cert:{用于ID} {商品ID} {购买数量}
1、添加商品

HSET cert:1001 2080 1

2、增加商品数量

HINCRBY cert:1001 2080 2

3、商品总数

HLEN cert:1001

4、删除商品

HDEL cert:1001 2080

5、获取购物车里的所有商品

HGETALL cert:1001

注意:展示的商品详情,通过ID去查询然后展示数据

3、list列表数据结构

LPUSH key value [value ...]
将一个或多个值value插入到ley列表的表头(最左边)

RPUSH key value [value ...]
将一个或多个值value插入到ley列表的表头(最右边)

LPOP key
移除并返回key列表的头元素

RPOP key
移除并返回key列表的尾元素

LRANGE key start stop
返回列表key中指定区间内的元素,区间以偏移量start和stop指定,注意从0开始

BLPOP key [key ...] timeout
从key列表的表头弹出一个元素,所列表中没有元素,阻塞等待timeout秒,如果timeout=0则一直阻塞等待

BRPOP key [key ...] timeout
从key列表的表尾弹出一个元素,若列表中没有元素,阻塞等待timeout秒,如果timeout=0则一直阻塞等待


--增 
lpush mykey a b --若key不存在,创建该键及与其关联的List,依次插入a ,b, 若List类型的key存在,则插入value中
rpush mykey a b --在链表尾部先插入b,在插入a(lpush list a b那么读的时候是b,a的顺序,而rpush是怎么放怎么读出来
--删
del mykey       --删除已有键 
--改
lset mykey 1 e        --从头开始, 将索引为1的元素值,设置为新值 e,若索引越界,则返回错误信息
--查
lrange mykey 0 -1 --取链表中的全部元素,其中0表示第一个元素,-1表示最后一个元素。
lrange mykey 0 2  --从头开始,取索引为0,1,2的元素
lpop mykey        --获取头部元素,并且弹出头部元素,出栈

List使用场景
常用分布式数据结构:
1、Stack栈 = LPUSH + LPOP

栈数据结构,先进后出

2、Queue队列 = LPUSH+ RPOP

队列数据结构,先进先出

3、Blocking MQ(阻塞队列) = LPUSH +BPROP

注意:BPROP可以实现阻塞 B->block

4、微博,微信公众号订阅消息流

(几万以下粉丝适合)
给每个用户,创建一个List接收消息列表,
然后发布的文章添加到用户的List数据结构中去,同时默认是拍好序号的数据,直接使用
LRANG msg:{微博ID} 0 5
则可以取出6条数据,这样用户访问的性能就很高
(注意:对于特别多订阅用户不适合)

超级大V,应该使用什么方案呢?

4、Set集合数据结构

Set常用操作:

SADD key member [member ...]
向集合key中存入元素。元素存在则忽略
可以批量的放元素

SREM key member [member ...]
从集合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中

--增
sadd myset a b c --若key不存在,创建该键及与其关联的set,依次插入a ,b,c。若key存在,则插入value中,若a 在myset中已经存在,则插入了 b 和 c 两个新成员。

--删
spop myset       --尾部的b被移出,事实上b并不是之前插入的第一个或最后一个成员
srem myset a d f --若f不存在, 移出 a、d ,并返回2

--改
smove myset myset2 a --将a从 myset 移到 myset2,

--查
sismember myset a --判断 a 是否已经存在,返回值为 1 表示存在。
smembers myset    --查看set中的内容
scard myset       --获取Set 集合中元素的数量
srandmember myset --随机的返回某一成员

Set数据结构使用场景:
1、微信抽奖小程序
①点击参与抽奖加入集合

SADD key {userID}
示例:活动act:001参与用户2001、2002
sadd act:001 2001
sadd act:001 2002

②查看参与抽奖所有用户

SMEMBERS key
示例:
SMEMBERS act:001
展示所有参与抽奖用户userId

③抽取count名抽奖者

SRANDMEMBER key [count]
从集合中随机拿出count个元素

SPOP key [count]
从集合中拿出元素并删除元素
比如抽奖后不能再参与抽奖了

使用场景:
微信微博点赞,收藏,标签
1、点赞

SADD like:{消息ID} {用户ID}

2、取消点赞

SREM like:{消息ID} {用户ID}

3、检查用户是否点过赞

SISMEMBER like:{消息ID} {用户ID}

4、获取点赞的用户列表

SISMEMBERS like:{消息ID}

5、获取点赞的用户数

SCARD like:{消息ID}

关注模型:比如共同关注,你的朋友也关注了它

5、ZSet支持排序的有序集合数据结构

ZSET常用操作:

ZADD key score member [ [score member] ... ]
向有序集合key中加入带分值的元素

ZREM key member [member ...]
从有序集合中删除元素

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 ...]
并集计算

ZINTERSTORE destkey numkeys key [key ...]
交集计算

--增
zadd key 2 "two" 3 "three" --添加两个分数分别是 2 和 3 的两个成员
--删
zrem key one two  --删除多个成员变量,返回删除的数量
--改
zincrby key 2 one --将成员 one 的分数增加 2,并返回该成员更新后的分数(分数改变后相应它的index也会改变)
--查 
zrange key 0 -1 WITHSCORES --返回所有成员和分数,不加WITHSCORES,只返回成员
zrange key start stop --按照元素的分值从小到大的顺序返回从start 到stop之间的所有元素
zscore key three --获取成员 three 的分数
zrangebyscore key 1 2 --获取分数满足表达式 1 < score <= 2 的成员
zcard key       --获取 myzset 键中成员的数量
zcount key 1 2  --获取分数满足表达式 1 <= score <= 2 的成员的数量
zrank key member --获取元素的排名,从小到大
zrevrank key member --获取元素的排名,从大到小

使用场景
1、ZSET点赞按照时间排序
2、ZSET集合操作实现排行榜
①点击新闻增加阅读数

ZINCRBY hotNews:20220103 1 领导实地考察

②展示当日排行前十(取出ZSET前10个元素)

ZREVRANGE hotNews:20220918 0 9 WITHSCORES

③七日搜索榜单计算(求并集)

ZUNIONSTORE hotNews:202200813-202200819 7
hotsNews:202200813 hotsNews:202200814 hotsNews:202200815
hotsNews:202200816 hotsNews:202200817 hotsNews:202200818
hotsNews:202200819

④展示七日排行前十

ZREVRANGE hotNews:202200813-202200819 0 9 WITHSCORES

6、Geospatial主要存储经纬度数据

Redis 在 3.2 推出 Geo 类型,该功能可以推算出地理位置信息,两地之间的距离。

7、HyperLogLogs

用于大数据量基数统计,速度非常快,占用内存非常小。每个HyperLogLog键只需要花费12KB内存,就可以计算接近 2^64个不同元素的基数。比如计算网站UV(User view,用户访问数量,一个用户一天访问同一个URL地址多次合并为一次)。

HyperLogLog常用于大数据量的统计:
1、页面访问量统计或者用户访问量统计

2、统计注册 IP 数

3、统计每日访问 IP 数

4、统计页面实时 UV 数

5、统计在线用户数

6、统计用户每天搜索不同词条的个数

说明:基数不大,数据量不大就用不上,会有点大材小用浪费空间
有局限性,就是只能统计基数数量,而没办法去知道具体的内容是什么
bitmap相比,属于两种特定统计情况,简单来说,HyperLogLog 去重比 bitmap 方便很多

一般可以bitmap和hyperloglog配合使用,bitmap标识哪些用户活跃,hyperloglog计数

8、bitmap

1、用户签到
很多网站都提供了签到功能,并且需要展示最近一个月的签到情况,这种情况可以使用 BitMap 来实现。
根据日期 offset = (今天是一年中的第几天) % (今年的天数),key = 年份:用户id。

如果需要将用户的详细签到信息入库的话,可以考虑使用一个一步线程来完成。

2、统计用户活跃数
使用日期作为 key,然后用户 id 为 offset,如果当日活跃过就设置为1。具体怎么样才算活跃这个标准大家可以自己指定。

假如 20201009 活跃用户情况是: [1,0,1,1,0]
20201010 活跃用户情况是 :[ 1,1,0,1,0 ]

统计连续两天活跃的用户总数:
bitop and dest1 20201009 20201010 
# dest1 中值为1的offset,就是连续两天活跃用户的ID
bitcount dest1

统计20201009 ~ 20201010 活跃过的用户:
bitop or dest2 20201009 20201010 

3、统计用户是否在线
如果需要提供一个查询当前用户是否在线的接口,也可以考虑使用 BitMap 。即节约空间效率又高,只需要一个 key,然后用户 id 为 offset,如果在线就设置为 1,不在线就设置为 0。
4、实现布隆过滤器

参考链接:谈谈Redis五种数据结构及真实应用场景
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,125评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,293评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,054评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,077评论 1 291
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,096评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,062评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,988评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,817评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,266评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,486评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,646评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,375评论 5 342
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,974评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,621评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,796评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,642评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,538评论 2 352

推荐阅读更多精彩内容