软件设计之Redis(3)
路线图推荐:
【Java学习路线-极速版】【Java架构师技术图谱】
尚硅谷Redis零基础到进阶,最强redis7教程,阳哥亲自带练(附redis面试题)
资料可以去尚硅谷官网免费领取
感谢学习笔记提供:github地址
学习内容:
- Hash操作命令
- Set操作命令
- Zset操作命令
- Bitmap操作命令
- HyperLogLog操作命令
- GEO操作命令
- Stream操作命令
- Bitfield操作指令
1、Hash操作命令
结构:
Hash 是一个键(key)到字段(field)及其值(value)的映射。
存储:
每个 Hash 可以包含多个字段,每个字段都对应一个值。Hash 的存储方式非常高效,尤其适合存储较小的对象。
HSET 命令
可以一次性添加多个字段和值到 Redis Hash 中。在使用 HSET 时,你可以提供一个键,后面跟着多个字段及其对应的值。
HSET user:1000 name "Alice" age 30 email "alice@example.com"
命令 | 作用 | 语法 | 返回值 | 示例 |
---|---|---|---|---|
HSET | 将字段和值设置到 Hash 中 | HSET key field value | 1(如果字段是新创建的)或 0(如果字段已存在) | HSET user:1000 name "Alice" |
HGET | 获取 Hash 中指定字段的值 | HGET key field | 字段的值,如果字段不存在则返回 nil | HGET user:1000 name ,返回 "Alice" |
HDEL | 删除 Hash 中指定的字段 | HDEL key field [field ...] | 被删除字段的数量 | HDEL user:1000 name ,删除字段 name |
HGETALL | 获取 Hash 中所有字段及其值 | HGETALL key | 以键值对的形式返回所有字段和对应的值 | HGETALL user:1000 ,返回 { "name": "Alice" } |
HKEYS | 获取 Hash 中所有字段的名称 | HKEYS key | 所有字段的名称 | HKEYS user:1000 ,返回 ["name"] |
HVALS | 获取 Hash 中所有字段的值 | HVALS key | 所有字段的值 | HVALS user:1000 ,返回 ["Alice"] |
HINCRBY | 将 Hash 中指定字段的值增加指定的整数 | HINCRBY key field increment | 增加后的值 | HINCRBY user:1000 age 1 ,将 age 增加 1 |
2、Set操作命令
Redis集合
是一种无序的字符串集合,具有唯一性,即集合中的元素不允许重复
。集合是 Redis 中的一种数据结构,非常适合用于需要快速查找、插入和删除的场景。
Set 集合中的元素没有固定顺序
,插入顺序不影响返回结果。
命令 | 作用 | 语法 | 返回值 | 示例 |
---|---|---|---|---|
SADD | 向集合中添加一个或多个元素 | SADD key member [member ...] | 被添加到集合中的新元素数量 | SADD myset "a" "b" "c" |
SREM | 从集合中移除一个或多个元素 | SREM key member [member ...] | 被移除的元素数量 | SREM myset "b" |
SMEMBERS | 获取集合中的所有元素 | SMEMBERS key | 集合中的所有元素 | SMEMBERS myset ,返回 ["a", "c"] |
SISMEMBER | 判断某个元素是否是集合的成员 | SISMEMBER key member | 如果是,返回 1 ;否则返回 0 | SISMEMBER myset "a" ,返回 1 |
SCARD | 获取集合中元素的数量 | SCARD key | 集合中元素的数量 | SCARD myset ,返回 2 |
SINTER | 获取一个或多个集合的交集 | SINTER key1 [key2 ...] | 交集中的元素 | SINTER set1 set2 |
SUNION | 获取一个或多个集合的并集 | SUNION key1 [key2 ...] | 并集中的元素 | SUNION set1 set2 |
SDIFF | 获取一个集合与其他集合的差集 | SDIFF key1 [key2 ...] | 差集中的元素 | SDIFF set1 set2 |
3、Zset操作命令
Redis 的有序集合(ZSet,Sorted Set)是一种将元素
与分数(score)
关联起来的数据结构,和普通集合不同,ZSet 是按照分数从小到大自动排序的,常用于排行榜、优先队列等场景。
在 Redis 的有序集合(ZSet)中,ZADD 命令根据元素的分数(score)进行排序,而不是像列表(List)那样通过插入顺序进行排序。因此,当使用 ZADD 添加元素时,Redis 会根据提供的分数从小到大排序元素,而与添加的顺序无关。
命令 | 描述 | 语法 | 示例 |
---|---|---|---|
ZADD | 添加元素并设置分数 | ZADD key score member [score member ...] | ZADD leaderboard 100 user1 200 user2 150 user3 |
ZRANGE | 按照分数从小到大,返回指定区间的元素 | ZRANGE key start stop [WITHSCORES] | ZRANGE leaderboard 0 2 WITHSCORES |
ZREVRANGE | 按照分数从大到小,返回指定区间的元素 | ZREVRANGE key start stop [WITHSCORES] | ZREVRANGE leaderboard 0 2 WITHSCORES |
ZSCORE | 返回指定成员的分数 | ZSCORE key member | ZSCORE leaderboard user1 |
ZRANK | 返回指定成员的排名(从小到大) | ZRANK key member | ZRANK leaderboard user1 |
ZREVRANK | 返回指定成员的排名(从大到小) | ZREVRANK key member | ZREVRANK leaderboard user1 |
ZINCRBY | 增加指定成员的分数 | ZINCRBY key increment member | ZINCRBY leaderboard 50 user1 |
ZREM | 移除一个或多个成员 | ZREM key member [member ...] | ZREM leaderboard user1 user2 |
ZRANGEBYSCORE | 按照分数范围返回成员 | ZRANGEBYSCORE key min max [WITHSCORES] | ZRANGEBYSCORE leaderboard 100 200 WITHSCORES |
ZREMRANGEBYSCORE | 移除分数范围内的成员 | ZREMRANGEBYSCORE key min max | ZREMRANGEBYSCORE leaderboard 100 150 |
ZREMRANGEBYRANK | 移除指定排名范围内的成员 | ZREMRANGEBYRANK key start stop | ZREMRANGEBYRANK leaderboard 0 1 |
ZCARD | 返回集合中的成员数量 | ZCARD key | ZCARD leaderboard |
ZCOUNT | 统计分数在指定范围内的成员数量 | ZCOUNT key min max | ZCOUNT leaderboard 100 200 |
ZINTERSTORE | 计算给定的有序集合的交集并存储在新集合中 | ZINTERSTORE dest numkeys key [key ...] | ZINTERSTORE result 2 key1 key2 |
ZUNIONSTORE | 计算给定的有序集合的并集并存储在新集合中 | ZUNIONSTORE dest numkeys key [key ...] | ZUNIONSTORE result 2 key1 key2 |
4、Bitmap操作命令
Bitmap
是 Redis 中用于存储和操作二进制位
的特殊数据结构,可以将一组二进制位
存储在一个键
下,并进行位级别的操作
。Bitmap 本质上是一个字符串,字符串的每一位都可以存储 0 或 1,因此它适合用于高效存储和处理大量布尔值的场景,例如用户签到、在线状态跟踪等。
命令 | 描述 | 语法 | 示例 |
---|---|---|---|
SETBIT | 设置指定位置的位值(0 或 1) | SETBIT key offset value | SETBIT user_activity 7 1 |
GETBIT | 获取指定位置的位值 | GETBIT key offset | GETBIT user_activity 7 |
BITCOUNT | 统计位值为 1 的数量 | BITCOUNT key [start end] | BITCOUNT user_activity |
BITOP | 对多个 Bitmap 执行位操作(AND、OR、XOR、NOT) | BITOP operation destkey key1 [key2 ...] | BITOP AND result_bitmap user1_activity user2 |
BITPOS | 查找第一个指定位值(0 或 1)的位置 | BITPOS key bit [start end] | BITPOS user_activity 1 |
setbit
假设设置k1 的1
号位为1,实际情况是:
bitpos
实现记录连续2天都签到的用户数量:
- 每个用户的签到情况对应一位(bit)
- 这两天的签到记录进行按位 与(AND) 操作。只有在两个 Bitmap 中该位都为 1 的用户,才算是连续两天都签到的用户
# 第一天的签到
SETBIT day1_signin 0 1
SETBIT day1_signin 1 1
SETBIT day1_signin 2 0
SETBIT day1_signin 3 1
# 第二天的签到
SETBIT day2_signin 0 1
SETBIT day2_signin 1 0
SETBIT day2_signin 2 1
SETBIT day2_signin 3 1
# 使用 BITOP AND 操作,计算两天都签到的用户
BITOP AND both_days_signin day1_signin day2_signin
# 统计连续两天都签到的用户数量
BITCOUNT both_days_signin
5、HyperLogLog操作命令
HyperLogLog 使用了一种基于哈希函数的概率算法
。它通过将输入数据进行哈希处理,并使用位运算来维护一个状态,以估计唯一元素
的数量。
命令 | 描述 | 语法 | 示例 |
---|---|---|---|
PFADD | 添加元素到 HyperLogLog | PFADD key element [element ...] | PFADD hll_key user1 user2 user3 |
PFCOUNT | 返回 HyperLogLog 估算的唯一元素数量 | PFCOUNT key [key ...] | PFCOUNT hll_key |
PFMERGE | 合并多个 HyperLogLog 的估算值 | PFMERGE destkey sourcekey [sourcekey ...] | PFMERGE merged_key hll_key1 hll_key2 |
实际演示:
6、GEO操作命令
Redis 的 GEO(地理空间)模块用于存储
和处理
地理位置数据,允许用户在给定的坐标(经度和纬度)上执行地理空间相关的操作。GEO 数据结构可以用于处理位置服务
、附近地点
查找等场景。
Redis 的 GEO 模块基于Geohashing
和 有序集合(ZSet)
的数据结构来存储和处理地理位置信息。
- Geohashing 是一种将
经纬度坐标
转换为字符串
表示的方法。这种方法将地球表面划分为网格,并用一个字符串来表示特定区域
。这个字符串的长度决定了位置的精确度,越长表示越精确的位置。 - Geohashing 的基本原理是将
经纬度的值
编码为二进制
,然后将二进制数据交替分割成两个部分,分别表示纬度和经度。通过对这些二进制数据进行分组和压缩,形成最终的哈希值。 - Redis 使用
有序集合
数据结构来存储地理空间元素。每个元素的分数(score)
是其Geohash 值
,元素的值(value)
是其对应的地理位置名称或 ID
[经纬度经过一定的计算得到Score,而不是把经纬度作为分数]。 - 由于有序集合是
按分数排序的
,因此可以利用其特性高效地进行范围查询。通过根据 Geohash 值的范围,可以快速找到位于某个特定区域内的所有元素。
命令 | 描述 | 语法 | 示例 |
---|---|---|---|
GEOADD | 将一个或多个地理空间元素添加到指定的键 | GEOADD key longitude latitude member | GEOADD locations 13.361389 38.115556 "Palermo" |
GEODIST | 计算两个地理空间元素之间的距离 | GEODIST key member1 member2 [unit] | GEODIST locations "Palermo" "Catania" km |
GEOHASH | 获取一个或多个地理空间元素的哈希值 | GEOHASH key member [member ...] | GEOHASH locations "Palermo" |
GEOPOS | 获取一个或多个地理空间元素的坐标 | GEOPOS key member [member ...] | GEOPOS locations "Palermo" |
GEORADIUS | 查找指定半径内的地理空间元素 | GEORADIUS key longitude latitude radius [unit] | GEORADIUS locations 15 37 200 km |
GEORADIUSBYMEMBER | 查找指定半径内的地理空间元素(以成员为中心) | GEORADIUSBYMEMBER key member radius [unit] | GEORADIUSBYMEMBER locations "Palermo" 200 km |
GEORADIUS key longitude latitude radius M|KM|FT|MI \[WITHCOORD] \[WITHDIST] \[WITHHASH] [COUNT count [ANY]
- 以给定的经纬度为中心,返回与中心的距离不超过给定最大距离的所有元素位置
- m-米、km-千米、ft-英寸、mi-英里(单位)
- WITHDIST: 在返回位置元素的同时, 将位置元素与中心之间的距离也一并返回。 距离的单位和用户给定的范围单位保持一致。
- WITHCOORD: 将位置元素的经度和维度也一并返回。
- WITHHASH:以 52 位有符号整数的形式, 返回位置元素经过原始 geohash 编码的有序集合分值。 这个选项主要用于底层应用或者调试,实际中的作用并不大
- COUNT 限定返回的记录数。
7、Stream操作命令
什么是消息队列
消息队列(Message Queue,MQ)
是一种通信方法,主要用于在分布式系统或不同应用程序之间传递数据或信息
。它允许应用程序或服务异步地交换消息,从而实现解耦和提高系统的可扩展性和可靠性。
- 基本概念
生产者(Producer): 负责创建和发送消息到消息队列的组件或应用程序。
消费者(Consumer): 从消息队列中读取和处理消息的组件或应用程序。
消息队列(Message Queue): 用于存储消息的中间数据结构,负责在生产者和消费者之间传递消息。 - 消息队列通常采用以下方式工作:
发送消息: 生产者将消息发送到消息队列。消息被存储在队列中,等待被消费者处理。
存储消息: 消息队列将收到的消息存储在内部缓存中,可能使用内存或持久化存储(如磁盘)来确保消息不会丢失。
接收消息: 消费者从消息队列中获取消息,处理后可以删除或标记为已处理。
异步处理: 生产者和消费者可以在不同的时间运行,消费者可以在消息到达后随时处理,而不必等待生产者发送完所有消息。
Redis 5.0前消息队列实现
在 Redis 5.0 之前,开发者通常使用以下几种方式来实现消息队列。尽管这些方式能够满足基本需求,但它们也存在一些局限性。
实现方式1: 使用 Redis 的列表数据结构,通过 LPUSH 和 RPOP 命令来实现生产者和消费者的消息队列。
- 生产者通过
LPUSH
将消息添加到列表的左侧。 - 消费者通过
RPOP
从列表的右侧读取消息。 - 缺点:
-
- 实现复杂度高:需要管理分数和顺序。
-
- 仍然面临消息丢失和确认消费的问题。
- 仍然面临消息丢失和确认消费的问题。
实现方式2: 使用 Redis 发布/订阅(Pub/Sub)
- 实现方式:使用 Redis 的发布/订阅机制,将消息发布到频道,所有订阅该频道的消费者都能接收到消息。
- 缺点:
-
- 不持久化:如果消费者在消息发布时未在线,将无法接收到消息。
-
- 无法确认消费:无法保证消费者是否已成功处理消息。
-
- 不支持消息排队:无法控制消息的处理顺序。
- 不支持消息排队:无法控制消息的处理顺序。
Stream底层原理
消费内容(Message Content)
消费组(Consumer group)包括:
消费组名称
:消费组的唯一标识符。游标(Last_delivered_id)
:用于跟踪消费组处理进度的游标(游标记录了消费组当前处理的最大消息 ID)。-
- 消费组游标: 反映整个消费组的处理进度,指向已处理的最大消息 ID。
-
- 消费者游标: 反映每个消费者的处理进度,指向该消费者最近处理的消息 ID。
待处理消息
:存储每个消费者的待处理消息列表,以及每个消费者的状态信息(如未确认消息的数量)。
消费者(Consumer)
待处理消息( Pending IDs):
Pending IDs
是指尚未被消费组中的消费者确认处理的消息。使用 Redis Stream 时,消费者读取消息后需要进行确认(XACK),如果不确认,这些消息将被视为待处理消息。
队列相关指令
指令 | 描述 | 示例 |
---|---|---|
XADD | 向 Stream 中添加一条消息。 | XADD mystream * field1 value1 field2 value2 |
XTRIM | 修剪 Stream,删除超过指定数量的旧消息。 | XTRIM mystream 1000 |
XREAD | 从 Stream 中读取消息。 | XREAD COUNT 5 STREAMS mystream 0 |
XREADGROUP | 从 Stream 中以消费组的方式读取消息。 | XREADGROUP GROUP mygroup consumer1 STREAMS mystream > |
XDEL | 删除指定的消息。 | XDEL mystream 1625244543235-0 |
XLEN | 获取 Stream 中的消息数量。 | XLEN mystream |
XRANGE | 获取指定范围内的消息。 | XRANGE mystream - + |
XREVRANGE | 以降序获取指定范围内的消息。 | XREVRANGE mystream + - |
xadd:
默认用星号表示自动生成ID,消息 ID 是由两部分组成的,Redis对于ID有强制要求,具体格式为 时间戳-序列号
,且后续ID不能小于前一个ID。
- 时间戳用于保证消息的唯一性和顺序。它代表消息被添加到 Stream 的确切时间,确保所有消息可以按照时间顺序进行处理。
- 当多个消息在同一毫秒内被添加到 Stream 时,序列号用于区分这些消息。序列号从 0 开始递增,确保在相同时间戳下,消息的唯一性和顺序性。
XRANGE key start end [COUNT count]:
用于获取消息列表(可以指定范围),忽略删除的消息
- start 表示开始值,-代表最小值,也可以是某个精确的ID比如:
1625244543235-0
- end 表示结束值,+代表最大值,也可以是某个精确的ID
- count 表示最多获取多少个值
XREAD \[COUNT count] [BLOCK milliseconds] STREAMS key [key ...] id [id ...]
- 只会返回大于指定ID的消息
COUNT
最多读取多少条消息- BLOCK是否以阻塞的方式读取消息,默认不阻塞。如果milliseconds设置为0,表示永远阻塞
阻塞与非阻塞
非阻塞:
非阻塞操作指的是客户端发出读取消息请求后,无论 Redis 中有没有新消息,都会立即返回结果。若 Stream 中没有符合条件的消息,则返回空结果。
- 特点:快速返回,如果没有消息则不会等待,直接返回空值,适合在消息存在时的读取需求。
阻塞:
阻塞操作指的是客户端发出读取请求后,如果 Stream 中没有新消息,则会一直等待,直到有新消息或达到指定的超时时间后返回结果。
- 特点:适用于需要等待新消息的场景,例如消息队列系统。当没有消息可读取时,客户端会一直等待,直到有新消息或达到超时时间。超时后,返回空结果。
消费组相关指令
指令 | 描述 | 示例 |
---|---|---|
XGROUP CREATE | 创建一个消费组。 | XGROUP CREATE mystream mygroup 0 |
XGROUP DESTROY | 删除一个消费组。 | XGROUP DESTROY mystream mygroup |
XGROUP SETID | 手动设置消费组的游标。 | XGROUP SETID mystream mygroup 1625244543235-3 |
XPENDING | 查看消费组中待处理的消息状态。 | XPENDING mystream mygroup |
XACK | 确认已处理的消息。 | XACK mystream mygroup 1625244543235-0 |
XCLAIM | 将待处理的消息重新分配给其他消费者。 | XCLAIM mystream mygroup consumer2 1000 1625244543235-0 |
XREADGROUP | 以消费组的方式从 Stream 中读取消息。 | XREADGROUP GROUP mygroup consumer1 STREAMS mystream > |
XAUTOCLAIM | 自动确认已超时的消息并将其重新分配给消费者。 | XAUTOCLAIM mystream mygroup consumer2 1000 0 1625244543235-0 |
xreadgroup
- “>”,表示从第一条尚未被消费的消息开始读取
- 消息队列的信息一旦被消费组里的一个消费者读取,就不能再被该组内其他消费者读取
不同消费组的消费者
可以消费同一条消息
如何保证消费者在发生故障或宕机再次重启后,仍然可以读取未处理完的消息?
该机制依赖于Pending Entries List(待处理消息列表)
和 消息确认机制(Acknowledgement)
流程:
消息读取和分配: 消费者通过 XREADGROUP 读取消息,消息会进入 PEL 列表,表示已经分配给某个消费者,但尚未确认处理完成。
消息确认: 消费者处理完消息后,通过 XACK 命令确认,消息从 PEL 中移除。如果未确认,消息依然保留在 PEL 中。
消费者宕机后的恢复:
- 宕机后重启,消费者可以使用 XPENDING 查看未确认的消息。
- 使用 XCLAIM 或重新读取这些未确认消息,继续处理它们。
保证机制: 消息不会丢失,未被 XACK 确认的消息可以被重新分配给其他消费者,保证消息可靠处理。
8、Bitfield操作指令
Redis 的 BITFIELD 命令主要用来操作字符串的二进制位(bit)
,可以看作是在 Redis 字符串里创建了一个“位数组”,你可以像操作数组一样,去读、写或修改某些位。它的常见用途是用来高效地存储和处理小的整数数据,尤其是在需要节省空间的情况下。
命令 | 描述 | 示例 |
---|---|---|
BITFIELD key GET type offset | 获取指定偏移量和长度的位段值 | BITFIELD mykey GET u8 0 (从第 0 位读取 8 位无符号整数) |
BITFIELD key SET type offset value | 设置指定偏移量和长度的位段值 | BITFIELD mykey SET i5 4 12 (从第 4 位开始的 5 位设置为 12) |
BITFIELD key INCRBY type offset inc | 对指定偏移量和长度的位段值进行递增操作 | BITFIELD mykey INCRBY u4 8 1 (从第 8 位开始的 4 位加 1) |
BITFIELD key OVERFLOW WRAP | 设置溢出行为为环绕(超过最大值后从头开始) | BITFIELD mykey OVERFLOW WRAP INCRBY u8 0 200 |
BITFIELD key OVERFLOW SAT | 设置溢出行为为饱和(超出最大值或最小值则保持极值) | BITFIELD mykey OVERFLOW SAT INCRBY i8 0 200 |
BITFIELD key OVERFLOW FAIL | 设置溢出行为为失败(溢出时操作失败) | BITFIELD mykey OVERFLOW FAIL INCRBY u8 0 200 |