[博学谷学习记录]超强总结,用心分享|架构 Redis数据类型选择&应用场景

文章内容是学习过程中的知识总结,如有纰漏,欢迎指正


一、Redis的Key的设计规范

1、key名设计

可读性和可管理性

以业务名(或数据库名)为前缀(防止key冲突),用冒号分隔,比如 业务名:表名:id

ugc:video:1

简洁性

保证语义的前提下,控制key的长度,当key较多时,内存占用也不容忽视,例如:

user:{uid}:friends:messages:{mid}简化为u:{uid}:fr:m:{mid}

不要包含特殊字符

反例:包含空格、换行、单双引号以及其他转义字符

2、避免bigkey

  • 情况一:键值对的值大小本身就很大,例如value为1MB的String数据类型。为了避免String类型的bigKey,在业务层,我们要尽量把String类型的大小控制在10KB以下。
  • 情况二:键值对的值是集合类型,集合元素个数非常多,例如包含100万个元素的Hash集合类型数据。为了避免集合类型的bigkey,对应的设计规范是,尽量把集合类型的元素个数控制在1万以下。

二、string字符串类型

  1. 介绍 :string 数据结构是简单的 key-value 类型。虽然 Redis 是用 C 语言写的,但是 Redis 并没有使用 C 的字符串表示,而是自己构建了一种 简单动态字符串(simple dynamic string,SDS)。相比于 C 的原生字符串,Redis 的 SDS 不光可以保存文本数据还可以保存二进制数据,并且获取字符串长度复杂度为 O(1)(C 字符串为 O(N)),除此之外,Redis 的 SDS API 是安全的,不会造成缓冲区溢出。
  2. 常用命令: set,get,strlen,exists,decr,incr,setex 等等。
  3. 应用场景 :一般常用在需要计数的场景,比如用户的访问次数、热点文章的点赞转发数量等等。
  • 单值缓存

    SET key  value

    GET key

  • 对象缓存

MSET user:1:name zimu user:1:balance 1888

MGET user:1:name user:1:balance

  •  分布式锁(「SET if Not eXists」)

SETNX product:10001 true // 返回1代表获取锁成功

SETNX product:10001 false // 返回0代表获取锁失败

.......执行业务操作

DEL product:10001 // 执行完业务 释放锁

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

  • 计数器

    INCR article:readcount:101

 三、hash类型(散列表)

 

 

  1. 介绍 :hash 类似于 JDK1.8 前的 HashMap,内部实现也差不多(数组 + 链表)。不过,Redis 的 hash 做了更多优化。另外,hash 是一个 string 类型的 field 和 value 的映射表,特别适合用于存储对象,后续操作的时候,你可以直接仅仅修改这个对象中的某个字段的值。 比如我们可以 hash 数据结构来存储用户信息,商品信息等等。
  2. 常用命令: hset,hmset,hexists,hget,hgetall,hkeys,hvals 等。
  3. 应用场景: 系统中对象数据的存储。
  • 对象缓存

    HMSET user {userId}:username zhangfei {userId}:password 123456

    HMSET user 1:username zhangfei 1:password 123456

    HMGET user 1:username 1:password

  • 电商购物车

 

  • 购物车操作

1)添加商品 ---> hset cart:1001 10088 1

2)增加数量 ---> hincrby cart:1001 10088 1

3) 商品总数 ---> hlen cart:1001

4) 删除商品---> hdel cart:1001 10088

5)获取购物车所有商品---> hgetall cart:1001

优点:

1)同类数据归类整合储存,方便数据管理

2)相比String操作消耗内存和cpu更小

3)相比String储存 更节省空间

缺点:

1)过期功能不能使用在field上,只能用在key上

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

四、list列表类型list列表类型

  1. 介绍list 即是 链表。链表是一种非常常见的数据结构,特点是易于数据元素的插入和删除并且且可以灵活调整链表长度,但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 LinkedList,但是 C 语言并没有实现链表,所以 Redis 实现了自己的链表数据结构。Redis 的 list 的实现为一个 双向链表,即可以支持反向查找和遍历,更方便操作,不过带来了部分额外的内存开销。
  2. 常用命令: rpush,lpop,lpush,rpop,lrange、llen 等。
  3. 应用场景: 发布与订阅或者说消息队列、慢查询。

 

  • 常用数据结构

    Stack(栈)= LPUSH(左边放) + LPOP(左边取) --> FILO

    Quece(队列)= LPUSH(左边放) + RPOP右边取)

    BLocking MQ(阻塞队列)= LPUSH(左边放) + BRPOP(右边阻塞取:没有数据就阻塞!)

  • 微博、朋友圈、公众号等,关注的文章列表展示

 

用户关注了北京本地宝 ,京城美味君等公众号,这些订阅号发布消息时,通过推或拉的方式把消息LPUSH放入redis中属于用户的list中。其中key为msg:{用户_ID}。当用户要获取大V们发的消息时,使用LRANGE 命令从队列中获取指定个数的订阅号信息

1)京城美味君发动态,消息ID为10001

LPUSH msg:{用户-ID} 10001

2)北京本地宝发动态,消息ID为10002

LPUSH msg:{用户-ID} 10002

3)查看最新订阅号消息

LRANGE msg:{用户-ID} 0 4

 五、set集合类型

  1. 介绍 : set 类似于 Java 中的 HashSet 。Redis 中的 set 类型是一种无序集合,集合中的元素没有先后顺序。当你需要存储一个列表数据,又不希望出现重复数据时,set 是一个很好的选择,并且 set 提供了判断某个成员是否在一个 set 集合内的重要接口,这个也是 list 所不能提供的。可以基于 set 轻易实现交集、并集、差集的操作。比如:你可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis 可以非常方便的实现如共同关注、共同粉丝、共同喜好等功能。这个过程也就是求交集的过程。
  2. 常用命令: sadd,spop,smembers,sismember,scard,sinterstore,sunion 等。
  3. 应用场景: 需要存放的数据不能重复以及需要获取多个数据源交集和并集等场景
  • 微信抽奖小程序

 

1)点击 参与抽奖 加入集合

SADD key {userID}

2)查看排行榜

SMEMBERS key

3)抽取count名中奖者

SRANDMEMBER key [count] / SPOP key [count]

  • 集合操作实现微博、微信关注模型

 首先了解一下set的集合操作,假如有三个集合

交集为:SINTER set1 set2 set3 ==> { c }

并集为:SUNION set1 set2 set3 ==> { a,b,c,d,e }

差集为:SDIFF set1 set2 set3 ==> { a }

差集计算方式:set1 - (set2并set3) = {a、b、c} - {b、c、d、e} = {a} 只保留a中单独存在的元素

共同关注A的人:可以用交集来实现

我可能认识的人:可以使用差集来实现,把我关注的人求差集

 六、sortedset有序集合类型

  1. 介绍: 和 set 相比,sorted set 增加了一个权重参数 score,使得集合中的元素能够按 score 进行有序排列,还可以通过 score 的范围来获取元素的列表。有点像是 Java 中 HashMap 和 TreeSet 的结合体。

  2. 常用命令: zadd,zcard,zscore,zrange,zrevrange,zrem 等。

  3. 应用场景: 需要对数据根据某个权重进行排序的场景。比如在直播系统中,实时排行信息包含直播间在线用户列表,各种礼物排行榜,弹幕消息(可以理解为按消息维度的消息排行榜)等信息。

 

  • Zset集合操作实现排行榜

 

1)点击新闻,为其分值+1

ZINCRBY hotNews:20210707 1 iphone13或有日落金玫瑰金

2)展示当日排行前10

ZREVRANGE hotNews:20210707 0 ,9 WITHSCORES

七、bitmap位图 类型

  1. 介绍 : bitmap 存储的是连续的二进制数字(0 和 1),通过 bitmap, 只需要一个 bit 位来表示某个元素对应的值或者状态,key 就是对应元素本身 。我们知道 8 个 bit 可以组成一个 byte,所以 bitmap 本身会极大的节省储存空间。

 

  1. 常用命令: setbitgetbitbitcountbitop

  2. 应用场景: 适合需要保存状态信息(比如是否签到、是否登录...)并需要进一步对这些信息进行分析的场景。比如用户签到情况、活跃用户情况、用户行为统计(比如是否点赞过某个视频)。

例:存储对比:

有1亿用户,5千万登陆用户,那么统计每日用户的登录数。每一位标识一个用户ID,当某个用户访问我们的网站就在Bitmap中把标识此用户的位设置为1。

这里做了一个使用set集合和BitMap存储的对比。

 时间再拉长一点

 

# SETBIT 会返回之前位的值(默认是 0)这里会生成 7 个位
127.0.0.1:6379> setbit mykey 7 1
(integer) 0
127.0.0.1:6379> setbit mykey 7 0
(integer) 1
127.0.0.1:6379> getbit mykey 7
(integer) 0
127.0.0.1:6379> setbit mykey 6 1
(integer) 0
127.0.0.1:6379> setbit mykey 8 1
(integer) 0
# 通过 bitcount 统计被被设置为 1 的位的数量。
127.0.0.1:6379> bitcount mykey
(integer) 2Copy to clipboardErrorCopied

 针对上面提到的一些场景,这里进行进一步说明。

使用场景一:用户行为分析 很多网站为了分析你的喜好,需要研究你点赞过的内容。

# 记录你喜欢过 001 号小姐姐
127.0.0.1:6379> setbit beauty_girl_001 uid 1

使用场景二:统计活跃用户

面试题:现在系统有亿级的活跃用户,为了增强用户粘性,该如何实现签到、日活统计?

使用时间作为 key,然后用户 ID 为 offset,如果当日活跃过就设置为 1

那么我该如何计算某几天/月/年的活跃用户呢(暂且约定,统计时间内只要有一天在线就称为活跃),有请下一个 redis 的命令

# 对一个或多个保存二进制位的字符串 key 进行位元操作,并将结果保存到 destkey 上。
# BITOP 命令支持 AND 、 OR 、 NOT 、 XOR 这四种操作中的任意一种参数
BITOP operation destkey key [key ...]

初始化数据:

127.0.0.1:6379> setbit 20210308 1 1
(integer) 0
127.0.0.1:6379> setbit 20210308 2 1
(integer) 0
127.0.0.1:6379> setbit 20210309 1 1
(integer) 0

统计 20210308~20210309 总活跃用户数: 1

127.0.0.1:6379> bitop and desk1 20210308 20210309
(integer) 1
127.0.0.1:6379> bitcount desk1
(integer) 1

统计 20210308~20210309 在线活跃用户数: 2

127.0.0.1:6379> bitop or desk2 20210308 20210309
(integer) 1
127.0.0.1:6379> bitcount desk2
(integer) 2

八、geo地理位置类型

Redis 3.2 中增加了对GEO类型的支持。GEO,Geographic,地理信息的缩写。该类型,就是元素的2维坐标,在地图上就是经纬度。redis基于该类型,提供了经纬度设置,查询,范围查询,距离查询,经纬度Hash等常见操作

应用场景:附近的人、摇一摇、附近的车、附近银行站点查询

 

环境要求

  1. redis版本需要3.2及以上
  2. 如果使用jedis操作redis,需要jedis版本为2.9及以上
  3. 如果使用spring data redis操作redis,需要spring data redis版本为1.8.0及以上

redis GEO常用命令

Tips: 在学习geo命令时会使用到经纬度坐标信息,可以在百度地图的拾取坐标系统中获取测试坐标信息,网址:拾取坐标系统

1. geoadd命令

为了进行地理位置相关操作, 我们首先需要将具体的地理位置记录起来, 这一点可以通过执行 geoadd 命令来完成, 该命令的基本格式如下:

GEOADD location-set longitude latitude name [longitude latitude name ...]

此命令用于添加位置信息到集合中

以下代码展示了如何通过 GEOADD 命令, 将武汉、襄阳、宜昌、枝江、咸宁等数个湖北省的市添加到位置集合 hubeiCities 集合里面

此处添加武汉的坐标信息到hubeiCities集合中

geoadd hubeiCities 114.32538 30.534535 wuhan

此处添加襄阳、枝江、咸宁的坐标信息到hubeiCities集合中

geoadd hubeiCities 112.161882 32.064505 xiangyang 111.305197 30.708127 yichang 111.583717 30.463363 zhijiang 114.295174 29.885892 xianning

2. geopos命令

此命令用于根据输入的位置名称获取位置的坐标信息,基本语法如下

GEOPOS location-set name [name ...]

案例:查询襄阳市的位置信息

geopos hubeiCities xiangyang
--结果如下【1为经度 2为纬度】
1) "112.16188341379165649"
2) "32.06450528704699821"

也可以一次查询多个位置的经纬度

geopos hubeiCities xiangyang wuhan
--襄阳的经纬度
1) 1) "112.16188341379165649"
   2) "32.06450528704699821"
--武汉的经纬度
2) 1) "114.32538002729415894"
   2) "30.53453492166421057"

3. geodist命令

此命令用于计算两个位置之间的距离,基本语法如下:

GEODIST location-set location-x location-y [unit]

可选参数 unit 用于指定计算距离时的单位, 它的值可以是以下单位的其中一个:

m 表示单位为米。 km 表示单位为千米。 mi 表示单位为英里。 ft 表示单位为英尺。

案例:分别以默认距离单位和指定距离单位计算襄阳和武汉的距离

--不指定距离单位
127.0.0.1:6381> geodist hubeiCities xiangyang wuhan
"266889.7642"
--指定距离单位km
127.0.0.1:6381> geodist hubeiCities xiangyang wuhan km
"266.8898"

4. georadius命令和georadiusbymember命令

这两个命令都可以用于获取指定范围内的元素,也即查找特定范围之内的其他存在的地点。比如找出地点A范围200米之内的所有地点,找出地点B范围50公里之内的所有地点等等。

这两个命令的作用一样, 只是指定中心点的方式不同: georadius 使用用户给定的经纬度作为计算范围时的中心点, 而 georadiusbymember 则使用储存在位置集合里面的某个地点作为中心点。

以下是这两个命令的基本语法

GEORADIUS location-set longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

GEORADIUSBYMEMBER location-set location radius m|km|ft|mi [WITHCOORD] [WITHDIST] [ASC|DESC] [COUNT count]

这两个命令的各个参数的意义如下:

m|km|ft|mi 指定的是计算范围时的单位;

如果给定了WITHCOORD,那么在返回匹配的位置时会将位置的经纬度一并返回;

如果给定了WITHDIST , 那么在返回匹配的位置时会将位置与中心点之间的距离一并返回;

在默认情况下, GEORADIUS 和 GEORADIUSBYMEMBER 的结果是未排序的, ASC 可以让查找结果根据距离从近到远排序, 而 DESC 则可以让查找结果根据从远到近排序;

COUNT参数用于指定要返回的结果数量。

下面通过案例分别演示georadius命令和georadiusbymember命令

GEORADIUS案例: 在hubeiCities位置集合中查找距离经纬度为112.927076 28.235653(长沙)500km以内的位置信息,查找结果中应包含不超过5个位置的坐标信息,距离信息,并按距离由近到远排序。 查询代码如下:

127.0.0.1:6381> georadius hubeiCities 112.927076 28.235653 500 km withcoord withdist asc count 5
-- 咸宁  距离目标位置226.67公里  
1) 1) "xianning"
   2) "226.6716"
   3) 1) "114.29517298936843872"
      2) "29.88589217282589772"
-- 枝江  距离目标位置279.91公里
2) 1) "zhijiang"
   2) "279.9154"
   3) 1) "111.58371716737747192"
      2) "30.46336248623112652"
-- 武汉  距离目标位置289.38公里
3) 1) "wuhan"
   2) "289.3798"
   3) 1) "114.32538002729415894"
      2) "30.53453492166421057"
-- 宜昌  距离目标位置316.68公里
4) 1) "yichang"
   2) "316.6777"
   3) 1) "111.30519658327102661"
      2) "30.70812783498269738"
-- 襄阳  距离目标位置432.18公里
5) 1) "xiangyang"
   2) "432.1767"
   3) 1) "112.16188341379165649"
      2) "32.06450528704699821"

GEORADIUSBYMEMBER案例: 在hubeiCities位置集合中查找距离襄阳200km以内的位置信息【这里指定的目标位置只能是hubeiCities中存在的位置,而不能指定位置坐标】,查找结果中应包含不超过2个位置的坐标信息,距离信息,并按距离由远到近排序。 查询代码如下:

127.0.0.1:6381> georadiusbymember hubeiCities xiangyang 200 km withcoord withdist desc count 2
-- 枝江  距襄阳186.38km
1) 1) "zhijiang"
   2) "186.3784"
   3) 1) "111.58371716737747192"
      2) "30.46336248623112652"
-- 宜昌  距襄阳171.40km
2) 1) "yichang"
   2) "171.3950"
   3) 1) "111.30519658327102661"
      2) "30.70812783498269738"


总结

Redis数据类型:

1.string字符串类型

2.hash类型

3.list列表类型

4.set集合类型

5.sortedset有序集合类型

6.bitmap位图类型

7.geo地理位置类型

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值