Redis地理空间(GEO)
简介
从版本 3.2 开始,Redis 引入了地理空间支持,允许用户在 Redis 中存储地理位置信息,并执行一些与地理位置相关的操作。
原理
将球体转换为平面,区块转换为一点
基本命令
1.GEOADD - 将一个或多个地理位置元素添加到指定的键中。每个位置由经度、纬度和成员名称组成。
- 用途:将一个或多个地理位置元素添加到指定的键中。
- 语法:GEOADD key longitude latitude member [longitude latitude member ...]
- 示例:
> GEOADD places 116.404 39.915 "Beijing"
(integer) 1
> GEOADD places -73.993 40.750 "New York"
(integer) 1
2.GEODIST - 计算两个给定成员之间的距离。可以使用不同的单位来返回结果(例如米、千米、英里等)。
- 用途:计算两个给定成员之间的距离。
- 语法:
GEODIST key member1 member2 [unit]
- 单位:
m
(米)、km
(千米)、mi
(英里)、ft
(英尺) - 示例:
> GEODIST places Beijing New York km
"10684.4115"
3.GEOPOS - 获取一个或多个成员的位置(即经度和纬度)。
- 用途:获取一个或多个成员的位置(即经度和纬度)。
- 语法:
GEOPOS key member [member ...]
- 示例:
> GEOPOS places Beijing
1) 1) "116.40400000000000178698"2) "39.91499999999999943155"
4.GEORADIUS - 在给定的地理位置周围查找所有成员,这些成员位于指定半径内。可以以米或千米为单位指定半径。
- 用途:在给定的地理位置周围查找所有成员,这些成员位于指定半径内。
- 语法:
GEORADIUS key longitude latitude radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
- 示例:
> GEORADIUS places 116.404 39.915 100 km WITHDIST
1) 1) "Beijing"2) "0.0000"
5.GEORADIUSBYMEMBER - 类似于 GEORADIUS
,但是是以另一个成员的位置为中心来查找附近的其他成员。
- 用途:类似于 GEORADIUS,但以另一个成员的位置为中心来查找附近的其他成员。
- 语法:GEORADIUSBYMEMBER key member radius m|km|ft|mi [WITHCOORD] [WITHDIST] [WITHHASH] [COUNT count] [ASC|DESC] [STORE key] [STOREDIST key]
- 示例:
> GEORADIUSBYMEMBER places Beijing 100 km WITHDIST
1) 1) "Beijing"2) "0.0000"
- 参数说明
WITHCOORD:返回每个成员的经度和纬度。
WITHDIST:返回每个成员与中心点的距离。
WITHHASH:返回每个成员的 Geohash 整数值。
COUNT count:限制返回的结果数量。
ASC/DESC:按距离升序或降序排列结果。
STORE key:将结果存储到指定的键中。
STOREDIST key:将结果的距离存储到指定的键中。
6.GEOHASH - 返回一个或多个位置元素的Geohash表示。
- 用途:用来表示一个地理位置的大致范围。
- 语法:GEOHASH key member [member ...]
- 示例:
# 查找距离北京 100 公里内的所有地点
> GEORADIUS places 116.404 39.915 100 km WITHDIST
1) 1) "Beijing"2) "0.0000"
数据存储
地理空间数据在 Redis 中是作为有序集合(sorted set)存储的。每个地理位置都有一个唯一的分数,这个分数是由地理位置的经度和纬度计算得出的。因此,Redis 能够高效地处理地理位置的查询。
应用场景
附近的人/地点搜索 : 可以快速找到某个地理位置周围的用户或兴趣点。
路径规划 : 虽然 Redis 不直接支持复杂的路径规划算法,但它可以用来辅助计算两点之间的直线距离。
地理围栏 : 当用户进入或离开特定区域时触发事件。
Redis 流(Stream)
简介
Redis 流(Stream)是从 Redis 5.0 版本开始引入的一种新的数据类型,用于处理实时数据流。流(Stream)数据类型非常适合用于构建消息队列、日志记录系统、事件溯源等应用场景。
主要特性
持久化:流中的数据会被持久化存储,即使 Redis 重启后数据也不会丢失。
追加只读:流是一个追加只读的数据结构,只能在尾部添加新数据,不能修改已有的数据。
多消费者组:支持多个消费者组(Consumer Groups),每个组可以有多个消费者(Consumers),实现消息的分布式消费。
消息确认:消费者可以确认已经处理的消息,未确认的消息可以被重新消费。
历史消息保留:可以根据需要配置保留一定数量的历史消息,超出的部分会被自动删除。
底层结构
一个消息链表,将所有加入的消息都串起来,每个消息都有一个唯一的 ID 和对应的内容。
1 | Message Content | 消息内容 |
2 | Consumer group | 消费组,通过XGROUP CREATE 命令创建,同一个消费组可以有多个消费者 |
3 | Last_delivered_id | 游标,每个消费组会有个游标 last_delivered_id,任意一个消费者读取了消息都会使游标 last_delivered_id 往前移动。 |
4 | Consumer | 消费者,消费组中的消费者 |
5 | Pending_ids | 消费者会有一个状态变量,用于记录被当前消费已读取但未ack的消息Id,如果客户端没有ack,这个变量里面的消息ID会越来越多,一旦某个消息被ack它就开始减少。这个pending_ids变量在Redis官方被称之为 PEL(Pending Entries List),记录了当前已经被客户端读取的消息,但是还没有 ack (Acknowledge character:确认字符),它用来确保客户端至少消费了消息一次,而不会在网络传输的中途丢失了没处理 |
常用命令
队列相关指令
1. XADD
- 用途:向流中添加一条消息。如果指定的Stream 队列不存在,则该命令执行时会新建一个Stream 队列.
- 语法:XADD key [MAXLEN/LIMIT] count message
- 示例:
> XADD mystream * sensor-id 1234 temperature 19.8
1626893541302-0
* 表示让 Redis 自动生成一个唯一的消息 ID。类似mysql里面主键auto_increment,后面顺序跟着一堆业务key/value。
sensor-id 1234 temperature 19.8 是消息的内容,键值对形式。
信息条目指的是序列号,在相同的毫秒下序列号从0开始递增,序列号是64位长度,理论上在同一毫秒内生成的数据量无法到达这个级别,因此不用担心序列号会不够用。millisecondsTime指的是Redis节点服务器的本地时间,如果存在当前的毫秒时间戳比以前已经存在的数据的时间戳小的话(本地时间钟后跳),那么系统将会采用以前相同的毫秒创建新的ID,也即redis 在增加信息条目时会检查当前 id 与上一条目的 id, 自动纠正错误的情况,一定要保证后面的 id 比前面大,一个流中信息条目的ID必须是单调增的,这是流的基础。 |
客户端显示传入规则: Redis对于ID有强制要求,格式必须是 时间戳-自增Id这样的方式,且后续ID不能小于前一个ID |
Stream的消息内容,也就是图中的Message Content它的结构类似Hash结构,以key-value的形式存在。 |
2. XREAD
- 用途:从一个或多个流中读取消息。只会返回大于指定ID的消息。
- 语法:XREAD [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
- 示例:
> XREAD COUNT 2 STREAMS mystream 0
1) 1) "mystream"2) 1) 1) "1626893541302-0"2) 1) "sensor-id"2) "1234"3) "temperature"4) "19.8"2) 1) "1626893541303-0"2) 1) "sensor-id"2) "1234"3) "temperature"4) "20.1"
count表示最多可以读取多少条消息
BLOCK表示是否以阻塞的方式读取消息,默认不阻塞,如果 milliseconds设置为0,表示 永远阻塞。
$代表特殊ID,表示以当前Stream已经存储的最大的ID作为最后一个ID,当前Stream中不存在大于当前最大ID的消息,因此此时返回nil |
0-0代表从最小的ID开始获取Stream中的消息,当不指定count,将会返回Stream中的所有消息,注意也可以使用0(00/000也都是可以的……) |
Stream的基础方法,使用xadd存入消息和xread循环阻塞读取消息的方式可以实现简易版的消息队列,交互流程如下:
3. XRANGE
- 用途:按时间顺序返回流中的消息。
- 语法:XRANGE key start end [COUNT count]
- 示例:
> XRANGE mystream - +
1) 1) "1626893541302-0"2) 1) "sensor-id"2) "1234"3) "temperature"4) "19.8"
2) 1) "1626893541303-0"2) 1) "sensor-id"2) "1234"3) "temperature"4) "20.1"
- 表示从最早的 ID 开始。
+ 表示到最新的 ID 结束。
4. XREVRANGE
- 用途:按逆时间顺序返回流中的消息。
- 语法:XREVRANGE key end start [COUNT count]
- 示例:
> XREVRANGE mystream + -
1) 1) "1626893541303-0"2) 1) "sensor-id"2) "1234"3) "temperature"4) "20.1"
2) 1) "1626893541302-0"2) 1) "sensor-id"2) "1234"3) "temperature"4) "19.8"
5.XDEL
- 用途:删除流中的一个或多个消息。
- 语法:XDEL key id [id ...]
- 示例:
> XDEL mystream 1626893541302-0
(integer) 1
6.XLEN
- 用途:获取流中的消息数量。
- 语法:XLEN key
- 示例:
> XLEN mystream
(integer) 3
如果尝试获取一个不存在的流的消息数量,XLEN 将返回 0。
消费组相关指令
1. XGROUP CREATE
- 用途:创建一个新的消费者组。
- 语法:XGROUP CREATE key groupname id-or-$
- 示例:
> XGROUP CREATE mystream group1 0
OK
group1 是消费者组的名称。
0 表示从最早的未确认消息开始消费。
$表示从Stream尾部开始消费
0表示从Stream头部开始消费
创建消费者组的时候必须指定 ID, ID 为 0 表示从头开始消费,为 $ 表示只消费新的消息,队尾新来
2. XREADGROUP
- 用途:从流中读取消息,支持消费者组。
- 语法:XREADGROUP GROUP groupname consumername [COUNT count] [BLOCK milliseconds] STREAMS key [key ...] ID [ID ...]
- 示例:
> XREADGROUP GROUP group1 consumer1 COUNT 2 STREAMS mystream >
1) 1) "mystream"2) 1) 1) "1626893541302-0"2) 1) "sensor-id"2) "1234"3) "temperature"4) "19.8"2) 1) "1626893541303-0"2) 1) "sensor-id"2) "1234"3) "temperature"4) "20.1"
GROUP group1 consumer1 表示从 group1 消费者组中读取,consumer1 是消费者名称。
> 表示从最新的未确认消息开始消费。
stream中的消息一旦被消费组中的一个消费者读取,就不能被该消费组内的其他成员读取了,即同一个消费组里的消费者不能消费同一条消息,不同消费组的消费者可以消费同一条消息。
让组内的多个消费者共同分担读取消息,我们通常会让每个消费者读取部分消息,从而实现消息读取负载在多个消费者间是均衡分布的。
3. XACK
- 用途:确认消息已被成功处理。
- 语法:XACK key groupname id [id ...]
- 示例:
> XACK mystream group1 1626893541302-0
(integer) 1
4. XINFO
- 用途:获取流的各种信息。
- 语法:XINFO subcommand key [extra arguments]
- 示例:
> XINFO STREAM mystream
1) length
2) (integer) 2
3) first-entry
4) 1) "1626893541302-0"2) 1) "sensor-id"2) "1234"3) "temperature"4) "19.8"
5) last-entry
6) 1) "1626893541303-0"2) 1) "sensor-id"2) "1234"3) "temperature"4) "20.1"
5.XPENDING
- 用途:查询每个消费组内所有消费者[已读取,但尚未确认]的消息,或查看某个消费者具体读取了哪些数据。
- 语法:XPENDING key groupname [start end count] [consumer]
- 示例:
> XPENDING mystream group1 - + 10 consumer1
1) 1) "1626893541302-0"2) "consumer1"3) (integer) 10004) (integer) 1
这表明 consumer1
有 1 条待处理消息。
应用场景
消息队列:使用 Redis 流可以构建高性能的消息队列系统,支持多消费者组和消息确认机制。
日志记录:流可以用于记录应用程序的日志,支持按时间顺序读取和归档。
事件溯源:流可以用于记录系统的状态变化,支持历史数据的查询和分析。