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、实现布隆过滤器
网友评论