Redis核心技术与实战【学习笔记】 - 7.Redis GEO类型 - 面向 LBS 应用的数据类型

前言

前面,介绍了 Redis 的 5 大基本数据类型:String、List、Hash、Set、Sorted Set,它们可以满足绝大多数的数据存储需求,但是在面对海里数据统计时,它们的内存开销很大。所以对于一些特殊的场景,它们是无法支持的。所以,Redis 还提供了 3 种扩展数据类型,分别是 Bitmap、HyperLogLog、GEO。今天再介绍下 GEO。


1.面向 LBS 应用的 GEO 数据类型

日常生活中,“附近停车场”、打车软件的叫车,这些都离不开基于位置信息服务(LBS)的应用。LBS 应用访问的数据是和人或物关联的一组经纬度信息,且要能查询相邻的经纬度范围,Redis 的 GEO 就非常适合应用在 LBS 服务的场景中。

1.1 GEO 底层结构

在设计一个数据类型的底层结构时,首先要知道,要处理的数据有什么访问特点。所以,需要先搞清楚位置信息到底是怎么存取的。

以叫车服务为例,来分析下 LBS 应用中经纬度的存取特点:

  1. 每辆网约车都有一个编号(如 1001),网约车需要将自己的经纬度信息(如 117.273521 , 39.884737)发给叫车应用。
  2. 用户在叫车的时候,叫车应用会根据用户的经纬度信息( 117.273521 , 39.884740)查找用户附件的车辆,并进行匹配。
  3. 等把位置相近的用户和车辆匹配上以后,叫车应用就会根据车辆的编号,获取车辆的信息,并返回给用户。

可以看到,一辆车(或一个用户)对应一组经纬度,并且随着车(或用户)的位置移动,相应的经纬度也会变化。

这种数据属于一个 key (例如车辆 ID) 对应一个 value(一组经纬度)。当有很多车辆信息需要保存时,就需要有一个集合来保存一系列的 key 和 value。Hash 集合类型可以快速存取一系列 key 和 value,正好可以记录一系列车辆 ID 和经纬度的对应关系,如下所示:
在这里插入图片描述
此外,Hash 类型的 HSET 操作,可以快速的更新车辆变化的经纬度信息。

目前看来,Hash 类型是一个不错的选择。但是,一个 LBS 应用除了记录经纬度信息外,还需要根据用户经纬度信息在车辆的 Hash 集合中进行范围查找。一旦涉及到范围查询,就意味着集合中的数据是有序的,但 Hash 类型是无需的,不能满足要求。

Sorted Set 类型也支持一个 key 对应一个 value 的记录模式,其中,key 就是 Sorted Set 中的元素,而 value 则是元素的权重分数。此外,Sorted Set 可以根据元素的权重分数排序,支持范围查询。这就能满足 LBS 服务中查找相邻位置的需求了。

而 GEO 类型的底层数据结构就是用 Sorted Set 来实现的。咱们还是接着叫车应用例子,用 Sorted Set 来保存车辆的经纬度信息时,Sorted Set 的元素是车辆 ID,元素的权重分数是经纬度信息,如下图所示:
在这里插入图片描述
此时问题是,Sorted Set 元素的权重分数是一个浮点数(float 类型),而一组经纬度包含的精度和纬度两个值,是没法直接保存为一个浮点数。这就要用到 GEO 类型中的 GeoHash 编码了。

1.2 GeoHash 编码方法

Redis 采用了 GeoHash 编码方法,这个方法的基本原理就是“二分区间,区间编码”。当我们要对一组经纬度进行 GeoHash 编码时,要先对经度和纬度分别编码,然后把经纬度各自的编码组合成一个最终编码。

首先,看下经度和纬度的单独编码过程。

  1. 对于一个地理位置来说,经度范围是 [-180, 180]。
  2. GeoHash 编码会把一个经度值编码成一个 N 位的二进制值,我们来对经度范围 [-180, 180] 做 N 次的二分区操作,其中 N 可以自定义。
  3. 在进行第一次区分时,经度范围 [-180, 180] 会被分成两个子区间:[-180, 0) 和 [0, 180]。此时,看一下编码的经度值是落在了左分区还是右分区。 如果是落在做分区,我们就用 0 表示;如果落在右分区,就用 1 表示。这样一来,每做完一次二分区,我们就可以得到 1 位编码值。
  4. 再对经度值所属的分区,再做一次二分区,同时再次查看经度落在了新二分区的做分区还是右分区,按照刚才的规则再做 1 位编码。当做完 N次的二分区后,经度值就可以用一个 N bit 的数来表示了。

举个例子,假设我们要编码的经度值是 117.273521 ,我们用 5 位编码值(也就是 N = 5,做 5 次分区)。
5. 先做第一次二分区操作,把经度区间 [-180, 180] 分成两个子区间:[-180, 0) 和 [0, 180],此时,经度值 117.273521 是属于右分区 [0, 180],所以,我们用 1 表示第一次二分区后的编码值。
6. 再做第二次二分区:把经度值 117.273521 所属分区 [0, 180] 区间,分成 [0, 90) 和 [90, 180]。此时经度值 117.273521 还是属于右分区 [90, 180],编码值仍为 1。
7. 第三次二分区,经度值 117.273521 落在了左分区 [90, 135) 中,所以,第三次分区后的编码值就是 0。
8. 第四次二分区,经度值 117.273521 落在了右分区 [112.5, 135]中,所以第四次编码值就是 1。
9. 第五次二分区,经度值 117.273521 落在了左分区 [112.5, 123.75) 中,所以第五次次编码值就是 0。

最终,做完 5 次分区后,我们把经度值 117.273521 的 GeoHash 编码值为 11010

对维度的编码方式,和对经度一样,只是经度的范围是 [-90, 90],GeoHash 编码的值为 10111,下标展示了对纬度值 39.884737 的编码过程。

分区次数最小维度值二分区中间值最大维度值维度39.884737所在区间维度的GeoHash编码
第一次-90090[0, 90]1
第二次04590[0, 45)0
第三次022.545[22.5, 45]1
第四次22.533.7545[33.75, 45]1
第五次33.7539.37545[39.375, 45)1

我们再把一组经纬度值都编完码后,再把它们组合在一起,组合的规则是:

  • 最终编码值的偶数位上依次是经度的编码值
  • 奇数位上依次是纬度的编码值
  • 其中偶数位从 0 开始,奇数位从 1 开始。

我们把刚刚计算的经纬度(117.273521, 39.884737)的各自编码值 11010 和 10111 ,组合之后:

  • 第 0 位 :经度的第 0 位 ,为 1
  • 第 1 位:纬度度的第 0 位,为 1
  • 第 2 位:经度的第 1 位,为 1
  • 依次类推,就能得到最终的编码值: 1 1 1 0 0 1 1 1 0 1(加粗的为经度编码值,其他是纬度编码值)

用了 GeoHash 编码后,原本无法表示权重的经纬度,就可以用 1110011101 这个值来表示,就可以保存为 Sorted Set 的权重分数了。

其实,使用 GeoHash 编码后,就相当于把整个地理空间划分成一个个放个,每个放个对应了一个 GeoHash 中的一个分区。举个例子。 我们把经度区间 [-180,180] 做一次二分区,把维度区间 [-90,90] 做一次二分区间,就会得到 4 个分区。我们看看经度和纬度范围及对应的 GeoHash 组合编码:

  • 分区一:[-180, 0) 和 [-90, 0),编码 00
  • 分区二:[-180, 0) 和 [0, 90),编码 01
  • 分区三:[0, 180) 和 [-90, 0),编码 10
  • 分区三:[0, 180) 和 [0, 90),编码 11

这 4 个分区对应了 4 个方格,每个方格覆盖了一定范围内的经纬度,分区越多,每个方格能覆盖到的地理位置就越小,也就越精准。我们把所有方格的编码值映射到一维空间内,相邻方格的 GeoHash 编码值基本也是接近的,如下所示:
在这里插入图片描述
所以,我们使用 Sorted Set 范围查询得到的相近编码值,在实际的地理空间上,也是相邻的方格,这就可以实现 LBS 应用搜索附件人或物的功能了。

不过,需要注意的是,有的编码值虽然在大小上接近,但实际对应的方格确距离比较远。例如,我们用 4 魏来做 GeoHash 编码,把经度区间 [-180,180] 和纬度区间 [-90,90] 分成了 4 个分区,一共 16 个分区。编码值为 0111 和 1000 的两个方格就离的比较远,如下所示:
在这里插入图片描述
所以,为了避免查询不准确问题,我们可以同时查询给定经纬度所在的方格周围的 4 个或 8 个方格。

好了,现在我们知道 GEO 类型是把经纬度所在区间编码作为 Sorted Set 中元素的权重分数,把和经纬度相关的车辆 ID 作为 Sorted Set 中元素本身的值保存下来,这样相邻经纬度的查询就可以通过编码值的大小范围来实现了。

1.3 如何操作 GEO 类型

在使用 GEO 类型的时,我们经常会用到两个命令,分别是 GETADD 和 GEORADIUS。

  • GETADD:把一组经纬度和相对应的一个 ID 记录到 GEO 类型集合中;
  • GEORADIUS:会根据输入的经纬度未知,查找这个纬度为中心的一定范围内的其他元素。

假设车辆 ID 是 1001,经纬度位置是 (117.273521, 39.884737),我们可以用一个 GEO 集合保存所有车辆的经纬度,集合 key 是 cars:locations。执行下面的这个命令,就可以把 ID 号为 1001 的车辆的当前经纬度位置存入 GEO 集合中:

GEOADD cars:locations 117.273521 39.884737 1001

当用户想要查看自己附近的网约车是,LBS 应用就可以使用 GEORADIUS 命令。例如,LBS 应用执行下面的命令时,Redis 会根据输入的用户的经纬度信息( 117.273521 , 39.884740),查找这个经纬度为中心的 5 公里内的车辆信息,并返回给 LBS 应用。

GEORADIUS cars:locations 117.273521 39.884740 5 km ASC COUNT 10

当然,你可以修改 “5” 这个参数,来返回更大或更小范围内的车辆信息。此外,还可以进一步限定返回的车辆信息。

  • 比如我们可以使用 ASC 选项,让返回的车辆按照距离这个中心位置从近到源的方式来排序,以边防选择最近的车辆;
  • 还可以使用 COUNT 选项,指定返回的车辆信息的数量。毕竟,5 公里内的车辆可能有很多,如果返回全部信息,会占用比较多的数据带宽,这个选项可以帮助控制返回的数据量,节省带宽。

可以看到,使用 GEO 数据类型可以非常轻松地操作经纬度这种信息。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/359552.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

计算机网络-物理层设备(中继器 集线器)

文章目录 中继器中继器的功能再生数字信号和再生模拟信号同一个协议 集线器(多口中继器)不具备定向传输的原因集线器是共享式设备的原因集线器的所有接口都处于同一个碰撞域(冲突域)内的原因 小结 中继器 中继器的功能 中继器的…

JVM 内存模型

1 什么是 JVM 内存模型 JVM 需要使用计算机的内存,Java 程序运行中所处理的对象或者算法都会使用 JVM 的内 存空间,JVM 将内存区划分为 5 块,这样的结构称之为 JVM 内存模型。 2 JVM 为什么进行内存区域划分 随着对象数量的增加&#xff…

基于二值化图像转GCode的单向扫描实现

基于二值化图像转GCode的单向扫描实现 什么是单向扫描单向扫描代码示例 基于二值化图像转GCode的单向扫描实现 什么是单向扫描 在激光雕刻中,单向扫描(Unidirectional Scanning)是一种雕刻技术,其中激光头只在一个方向上移动&a…

【Vue】二、Vue 组件展示控制的优雅解决方案

vue项目中展示的组件,我平常都是通过v-show进行展示控制,类似这样 通常情况下,一个正常展示组件的流程,是通过前端用户点击触发函数,在函数中对data数据进行操作,从而展示不同的页面 showWork: false, sho…

如何用Docker+jenkins 运行 python 自动化?

1.在 Linux 服务器安装 docker 2.创建 jenkins 容器 3.根据自动化项目依赖包构建 python 镜像(构建自动化 python 环境) 4.运行新的 python 容器,执行 jenkins 从仓库中拉下来的自动化项目 5.执行完成之后删除容器 前言 环境准备 Linux 服务器一台(我的是 CentOS7)…

GitHub工作流的使用笔记

文章目录 前言1. 怎么用2. 怎么写前端案例1:自动打包到新分支前端案例2:自动打包推送到gitee的build分支案例3:暂时略 前言 有些东西真的就是要不断的试错不断地试错才能摸索到一点点,就是摸索到凌晨两三点第二天要8点起床感觉要…

通过例子说明-动态规划

选择>行动>思考,好像是个死循环 -song。 动态规划(Dynamic Programming,简称DP)是一种解决问题的数学优化方法,通常用于解决具有重叠子问题和最优子结构性质的问题。它的基本思想是将问题拆分成小的子问题&#…

革新性技术:基于搜索操作的存内计算

文章目录 革新性技术:基于搜索操作的存内计算CSDN首个存内计算开发者社区NVALT:近似查找表的艺术MAP:存内计算的未来SQL-PIM:数据库的未来内存计算架构与技术小结从NVALT到NVQuery:存内计算的探索与前景NVQuery&#x…

242. 有效的字母异位词(力扣)(C语言题解)

✨欢迎来到脑子不好的小菜鸟的文章✨ 🎈创作不易,麻烦点点赞哦🎈 所属专栏:刷题 我的主页:脑子不好的小菜鸟 文章特点:关键点和步骤讲解放在 代码相应位置 题目链接: 242. 有效的字母异位词 …

UE5 C++ 读取本地图片并赋值到UI上

目录 结果图 节点样式 主要代码 调试代码 结果图 节点样式 主要代码 (注释纯属个人理解,可能存在错误) // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreMinimal.h&q…

Mysql进阶篇

1.Mysql服务架构 连接层: 处理客户端连接请求,对用户进行认证 服务层: 可以接收sql,调用存储过程,优化sql,缓存数据.... 引擎层: 负责实际与文件层进行交互操作的,可以有不同的引擎选择. 物理文件层: 存储表数据 以及 各种日志文件. 2.Mysql引擎 存储引擎就是存储数据&…

TSINGSEE青犀视频智慧电梯管理平台,执行精准管理、提升乘梯安全

一、方案背景 随着城市化进程的不断加快,我国已经成为全球最大的电梯生产和消费市场,电梯也成为人们日常生活中不可或缺的一部分。随着电梯数量的激增,电梯老龄化,维保数据不透明,物业管理成本高,政府监管…

StarRocks-3.1.0 单节点部署

1. 相关环境准备 FE: /opt/starrocks BE: /opt/starrocks 安装包下载 wget https://releases.starrocks.io/starrocks/StarRocks-3.1.0.tar.gz解压缩 tar -zxvf StarRocks-3.1.0.tar.gz 安装jdk (v2.5 及以上版本建议安装 JDK 11,我们使用…

腾讯mini项目总结-指标监控服务重构

项目概述 本项目的背景是,当前企业内部使用的指标监控服务的方案的成本很高,无法符合用户的需求,于是需要调研并对比测试市面上比较热门的几款开源的监控方案(选择了通用的OpenTelemetry协议:Signoz,otel-…

MedSAM:深度学习通用医学影像分割模型,更快、更准确地自动识别诊断疾病

MedSAM是一款基于深度学习的医学影像分割工具,它能够自动识别和描绘医学影像中的重要区域,如肿瘤或其他组织的病变。该工具通过学习大量医学影像和对应的掩模(即正确的分割结果),能够处理各种不同的医学影像和复杂情况…

数据库之TiDB基础讲解

文章目录 1 TiDB1.1 引言1.2 TiDB介绍1.3 系统架构1.3.1 TIDB Server1.3.2 PD Server1.3.3 TIKV Server1.3.4 TiKV如何不丢失数据1.3.5 分布式事务支持 1.4 与MySQL的对比1.5 性能测试1.5.1 测试一1.5.2 系统测试报告 2 1 TiDB 1.1 引言 当我们使用 Mysql 数据库到达一定量级…

使用nginx对视频、音频、图片等静态资源网址,加token签权

目前很多静态资源,都可以无权限验证,进行访问或转发,对有价值的资源进行签权,限制转发无法在代码中实现拦截,我们可以使用nginx对视频、音频、图片等静态资源网址,加token签权 如: http://192…

Win10 双网卡实现同时上内外网

因为需要同时上内网和外网,但公司做了网络隔离,不能同时上内外网,所以多加了块无线网卡,配置双网关实现同时上内外网,互不影响 打开 Windows PowerShell(管理员),输入:ro…

CCF-CSP 202312-2 因子化简(Java、C++、Python)

文章目录 因子化简题目背景问题描述输入格式输出格式样例输入样例输出样例解释子任务 满分代码JavaCPython线性筛法 因子化简 题目背景 质数(又称“素数”)是指在大于 1 的自然数中,除了 1 和它本身以外不再有其他因数的自然数。 问题描述…

房屋租赁系统-java

思维导图:业务逻辑 类的存放: 工具类 Utility package study.houserent.util; import java.util.*; /***/ public class Utility {//静态属性。。。private static Scanner scanner new Scanner(System.in);/*** 功能:读取键盘输入的一个菜单…