Redis 数据类型全攻略:解锁 Streams、Geospatial、HyperLogLog、Bitmaps 和 Bitfields

Hello , 大家好 , 这个专栏给大家带来的是 Redis 系列 !
本篇文章给大家讲解的是 Redis 中其他常见的数据类型的介绍 , 我们还给大家介绍一种 Redis 中的渐进式命令 , 让操作 Redis 更加安全 , 避免将 Redis 服务器阻塞住 .

在这里插入图片描述

本专栏旨在为初学者提供一个全面的 Redis 学习路径,从基础概念到实际应用,帮助读者快速掌握 Redis 的使用和管理技巧。通过本专栏的学习,能够构建坚实的 Redis 知识基础,并能够在实际学习以及工作中灵活运用 Redis 解决问题 .
专栏地址 : Redis 入门实践


一 . 其他类型

1.1 streams 类型

streams 类型是专门用来追加日志的类型 , 可以将事件分发给不同的目标 .

事件简单点说指的就是一些情况 , 我们也没办法预知它什么时候出现 . 但是等它出现之后我们该采取什么动作就采取什么动作

具体的命令我们也不去关心了 , streams 使用场景不大 , 而且面试也不常考 .
后续工作用到了 , 我们就可以去 Redis 官网来去阅读文档

1.2 geospatial 类型

geospatial 描述的是地理位置信息 .
我们描述一个具体的位置 , 都会以经纬度的概念来去描述 , 那 geospatial 就是用来存储经纬度坐标的 .
存储了一些位置之后 , 就可以让用户给定一个坐标 , 从刚才存储的点附近寻找 (按照半径 / 矩形区域) , 主要应用在地图相关的业务中
image.png
那具体的操作我们也可以从 Redis 官方文档中学习
https://redis.io/docs/data-types/geospatial/
image.png

1.3 HyperLogLog 类型

它的应用场景 , 只有一个 : 估算集合中不重复的元素个数

Set 有一个应用场景 , 统计服务器的 UV (用户访问的次数)
但是使用 Set 存在一个最大的问题 , 如果 UV 的数据量特别大 , Set 就会消耗很多的内存空间
假设 Set 里面存储 userId , 每个 userId 按照 8 个字节算
1 亿 UV -> 8 亿字节 -> 0.8 G -> 800 MB

而 Hyperloglog 可以最多使用 12KB 空间 , 来实现上面的效果
之所以 Set 需要消耗这么大的空间 , 这是因为 Set 需要存储每个元素 , 而 Hyperloglog 并不存储具体的元素 , 但是能够记录 “元素的特征” , 从而在新增元素的时候 , 能够知道当前新增的元素 , 是一个已经存在的元素还是一个崭新的元素 .
所以 Hyperloglog 最大的特点就是用来计数 (用来记录出当前集合中有多少个不同的元素) , 但是不能告诉你具体的元素都是什么
那 Hyperloglog 它的内部实现原理是 “位操作” , 是存在一定误差的 .

官方文档也详细给出了误差的大概范围 : https://redis.io/docs/data-types/probabilistic/hyperloglogs/
image.png

我们来看一下它的用法
添加元素 : pfadd key value

并不真正的添加元素本身的值 , 只是去记录该元素的特征 , 用来判断集合中是否存在

获取集合元素个数 : pfcount members

127.0.0.1:6379> pfadd members 123 # 添加元素
(integer) 1
127.0.0.1:6379> pfcount members # 获取集合元素个数
(integer) 1
127.0.0.1:6379> 

1.4 bitmaps 类型

位图就是使用 bit 位来表示整数
举个例子 :
image.png
位图本质上就还是一个集合 , 它属于是 Set 类型针对整数的特殊版本 , 目的就是节省空间

位图当中保存的数字都必须是整数 , 所以被称为 Set 类型的特殊版本

那我们刚才介绍的 Hyperloglog 不是也省空间吗 ?

Hyperloglog 既可以存储数字 , 也可以存储字符串 . 但是 Hyperloglog 不存储元素内容 , 只是计数效果
而 bitmaps 他是可以存储元素的

我们可以借助官网给我们的例子来了解 bitmaps 的语法
https://redis.io/docs/data-types/bitmaps/
image.png
我们现在有 1000 个主机需要监控 , 把这些主机标记成 0~999 , 我们现在需要快速地判断当前给定主机在 1h 以内是否监控过了
那我们就可以使用位图

127.0.0.1:6379> setbit pings:2023-08-08-17:00 123 1 # 把 123 这一位设置成 1
(integer) 0
127.0.0.1:6379> getbit pings:2023-08-08-17:00 123 # 查询 123 机器是否监测过了
(integer) 1 # 返回值为 1 代表被监测过了
127.0.0.1:6379> getbit pings:2023-08-08-17:00 456 # 查询 456 机器是否监测过了
(integer) 0 # 返回值为 0 代表未被监测过

1.5 bitfields

bitfields 表示位域 , 他可以理解成是一串二进制序列 (字节数组) , 同时可以把这个字节数组中的某几个位赋予特定含义 , 并且可以进行读取 / 修改 / 算术运算等相关操作

可以类比 C 语言中的位段进行理解
struct Test {
int a:8;
int b:16;
int c:8;
}
这里的数字 , 就描述了这个成员实际上占几个 bit 位

他的目的仍然是为了节省一定的空间
我们可以看一下官方文档提供的例子 : https://redis.io/docs/data-types/bitfields/
image.png
他的意思是你现在设计了一个网游 , 你想要给每个用户分配两个属性 : 金钱数和杀敌数 , 你的游戏很火很火 , 所以你需要一个至少 32 位的字节数组
image.png
新玩家刚开始有 1000 金币

# 设置该玩家初始金币
# u32 代表 unsigned int , 利用无符号整形来去操作 32 个比特位
# #0 相当于一个名字 , 相当于玩家的金钱
# 1000 给 #0 也就是玩家的金钱设置成 1000
127.0.0.1:6379> bitfield player:1:stats set u32 #0 1000
1) (integer) 0

image.png
杀死一个哥布林 , 获得 50 金

# 金币增加 , 杀敌数增加
# incrby u32 #0 50 代表利用无符号整形去让 #0(玩家金币) 增加 50
# incrby u32 #1 1 代表利用无符号整形去让 #1(玩家杀敌数) 增加 1
127.0.0.1:6379> bitfield player:1:stats incrby u32 #0 50 incrby u32 #1 1
1) (integer) 1050
2) (integer) 1

image.png
花 999 金币买了个装备

# 花 999 金币买了个装备
# incrby u32 #0 -999 表示利用无符号整形去让 #0(玩家金币) 减少 999
127.0.0.1:6379> bitfield player:1:stats incrby u32 #0 -999
1) (integer) 51

image.png
查询一下玩家的金币数和杀敌数

# 查询一下玩家的金币数和杀敌数
# get u32 #0 get u32 #1 表示利用无符号整数来去获取 #0(金币数) 和 #1(杀敌数) 的值
127.0.0.1:6379> bitfield player:1:stats get u32 #0 get u32 #1
1) (integer) 51
2) (integer) 1

二 . 渐进式遍历

我们之前在介绍过 keys 这个命令的时候 , 提到了渐进式遍历这个命令 .
keys 会一次性的把整个 Redis 中的所有的 key 都去获取到 , 但是这个操作比较危险 , 可能一下子会得到非常多的 key , 就会将 Redis 服务器阻塞住 .
那我们通过渐进式遍历 , 就可以逐步的获取到全部的 key , 并且不会阻塞住服务器 .

就类似于 MySQL 中的 limit N offset M 操作 , 每次获取一小部分数据

渐进式遍历其实是一组命令 , 但是一组命令中都非常类似 , 我们以 scan 举例
渐进式遍历.png

渐进式遍历.png

那接下来我们通过 Redis 官网看一下 scan 命令的用法 : https://redis.io/commands/scan/
语法 : scan cursor [Match pattern] [Count count] [Type type]

  1. cursor 并不是实际意义的下标 , 只是一个虚拟意义的光标
  2. [Match pattern] 指的是匹配的模式 , 跟我们之前学习过的 keys pattern 都是一样的用法
  3. [Count count] 限制这一次遍历能够获取到多少个元素 , 默认是 10 . 但是他与 MySQL 中的 limit 不一样 , MySQL 中的 limit 他是非常精确的 , 绝对会返回 <= limit 个数据 . 但是 Redis 中的 count 只能提供一个建议 , 实际返回的 key 的个数并不一定完全与 count 相同

image.png

  1. [Type type] : 虽然说 Redis 的 key 都是 string 类型 , 但是 value 的类型各不相同 . type 的作用是根据 value 的类型继续进行筛选
127.0.0.1:6379> mset k1 111 k2 222 k3 333 k4 444 k5 555 k6 666 k7 777 k8 888 k9 999 k10 1000 k11 11111 k12 22222 k13 33333
OK
127.0.0.1:6379> scan 0 # 利用光标来去获取元素
1) "7" # 下次 scan 光标的位置
# 本次遍历获取到的 key
2)  1) "k6"
    2) "k7"
    3) "k13"
    4) "k12"
    5) "k5"
    6) "k1"
    7) "k3"
    8) "k2"
    9) "k9"
   10) "k10"

因为 scan 命令默认就打印 10 个元素左右 , 所以我们并不知道所有的 key 是否已经打印完毕 , 那我们就可以根据下次 scan 光标的位置来去判断是否还有剩余元素 , 为 0 的时候就代表元素已经遍历结束了
然后我们还可以加上 count 选项

127.0.0.1:6379> scan 0 count 3 # 利用光标来去获取三个元素
1) "1" # 表示下次 scan 光标的位置
2) 1) "k6"
   2) "k7"
   3) "k13"
127.0.0.1:6379> scan 1 count 3 # 那我们这次光标就应该按照上一次提示的位置开始遍历
1) "9" # 表示下次 scan 光标的位置
2) 1) "k12"
   2) "k5"
   3) "k1"
127.0.0.1:6379> scan 9 count 3 # 那我们这次光标就应该按照上一次提示的位置开始遍历
1) "11" # 表示下次 scan 光标的位置
2) 1) "k3"
   2) "k2"
   3) "k9"
127.0.0.1:6379> scan 11 count 3 # 那我们这次光标就应该按照上一次提示的位置开始遍历
1) "0" # 此时代表元素已经遍历结束
2) 1) "k10"
   2) "k8"
   3) "k11"
   4) "k4"

那在使用 scan 命令的时候 , 还要注意一些事项

  1. count 不用每一次都设置成一样
  2. 渐进式遍历在遍历过程中不会在服务器这边存储任何的状态信息 , 也就是输随时可以终止 , 不会对服务器产生任何影响
  3. 渐进性遍历 scan 虽然解决了阻塞的问题 , 但如果在遍历期间键有所变化 (增加、修改、删除) , 可能就会导致遍历的时候键重复遍历或者遗漏 .

对于 Redis 常见命令我们已经介绍完毕 , 不知道大家吸收了多少 .
如果对您有帮助的话 , 还请一键三连~
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

加勒比海涛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值