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

本文主要是介绍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 数据类型可以非常轻松地操作经纬度这种信息。

这篇关于Redis核心技术与实战【学习笔记】 - 7.Redis GEO类型 - 面向 LBS 应用的数据类型的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/664459

相关文章

网页解析 lxml 库--实战

lxml库使用流程 lxml 是 Python 的第三方解析库,完全使用 Python 语言编写,它对 XPath表达式提供了良好的支 持,因此能够了高效地解析 HTML/XML 文档。本节讲解如何通过 lxml 库解析 HTML 文档。 pip install lxml lxm| 库提供了一个 etree 模块,该模块专门用来解析 HTML/XML 文档,下面来介绍一下 lxml 库

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

水位雨量在线监测系统概述及应用介绍

在当今社会,随着科技的飞速发展,各种智能监测系统已成为保障公共安全、促进资源管理和环境保护的重要工具。其中,水位雨量在线监测系统作为自然灾害预警、水资源管理及水利工程运行的关键技术,其重要性不言而喻。 一、水位雨量在线监测系统的基本原理 水位雨量在线监测系统主要由数据采集单元、数据传输网络、数据处理中心及用户终端四大部分构成,形成了一个完整的闭环系统。 数据采集单元:这是系统的“眼睛”,

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

csu 1446 Problem J Modified LCS (扩展欧几里得算法的简单应用)

这是一道扩展欧几里得算法的简单应用题,这题是在湖南多校训练赛中队友ac的一道题,在比赛之后请教了队友,然后自己把它a掉 这也是自己独自做扩展欧几里得算法的题目 题意:把题意转变下就变成了:求d1*x - d2*y = f2 - f1的解,很明显用exgcd来解 下面介绍一下exgcd的一些知识点:求ax + by = c的解 一、首先求ax + by = gcd(a,b)的解 这个

hdu1394(线段树点更新的应用)

题意:求一个序列经过一定的操作得到的序列的最小逆序数 这题会用到逆序数的一个性质,在0到n-1这些数字组成的乱序排列,将第一个数字A移到最后一位,得到的逆序数为res-a+(n-a-1) 知道上面的知识点后,可以用暴力来解 代码如下: #include<iostream>#include<algorithm>#include<cstring>#include<stack>#in