当前位置: 首页 > news >正文

Redis 地理散列GeoHash

用数据库来算附近的人

地图元素的位置数据使用二维的经纬度表示,经度范围(-180,180],纬度范围 (-90,90],纬度正负以赤道为界,北正南负,经度正负已本初子午线(英国格林尼治天文台)为界,东正西负。

当两个元素的距离不是很远时,可以直接使用勾股定理就能算得元素之间的距离。我们平时使用的【附近的人】的功能,元素距离都不是很大,勾股定理算距离就足够。现在,如果要计算【附近的人】,也就是给定一个元素的坐标,然后计算这个坐标附近的其他元素,按照距离进行排序,需要如何着手。

如果现在元素的经纬度坐标使用关系数据库(元素id,经度x,纬度y)存储,首先,不可能遍历来计算所有的元素和目标元素的距离然后再进行排序。这个计算量太大了,性能指标无法满足。一般方法都是通过矩形区域来限定元素的数量,然后对区域内的元素进行全量距离计算再排序。 这样可以明显减少计算量,如何划分矩形区域,可以指定一个半径为r,使用一条SQL就可以圈出来,当用户对筛选出来的结果不满意,就扩大半径继续筛选。
在这里插入图片描述

select id 
from positions 
where x0-r < x < x0+r and y0-r < y < y0+r

为了满足高性能的矩阵区域算法,数据表需要在经纬度坐标加上双向复合索引(x,y),这样可以最大优化查询性能。
但是数据库查询性能毕竟有限,如果【附近的人】查询请求非常多,在高并发场合下,可能不是一个很好的方案。

GeoHash算法

GeoHash算法将二维的经纬度数据映射到一维的整数,这样所有的元素都将在挂载到一条线上,距离靠近的二维坐标映射到一维后的点之间距离也会很接近。当我们想要计算【附近的人时】,首先将目标位置映射到这条线上,然后在这个一维的线上获取附近的点就可以了。

映射算法将整个地球看成一个二维平面,然后划分成一系列的正方形的方格,就好比围棋棋盘。所有的地图元素坐标都将放置于唯一的方格中。方格越小,坐标越精确。然后对这些方格进行整数编码,越是靠近的方格编码越是接近。如何编码最简单的方案就是切蛋糕法,设想一个正方形的蛋糕摆在面前,二刀下去均匀分成四块小正方形,这四个小正方形分别标记为00,01,10,11四个二进制整数。然后对每个小正方形继续用二刀法切割下去,这时每个小小正方形就可以使用4bit的二进制整数予以表示,然后继续切下去,正方形会越来越小,二进制整数也会越来越长,精确度就会越来越高。

编码之后,每个地图元素的坐标都将变成一个整数,通过这个整数可以还原出元素的坐标,整数越长,还原出来的坐标值的损失程度就越小。对于【附近的人】这个功能而言,损失的一点精确度可以忽略不计。

GeoHash算法会继续对这个整数做一次base32编码(0-9,a-z去掉a,i,l,o四个字母)变成一个字符串。在Redis里面,经纬度使用52位的整数进行编码,放进zset里面,zset的value是元素的key,score是GeoHash的52位整数值。zset的scire虽然是浮点数,但是对于52位的整数值,他可以无损存储。

在使用Redis进行Geo查询时,我们要时刻想到他的内部结构实际上是一个zset。通过zset的score排序就可以得到坐标附近的其他元素,通过将score还原成坐标值就可以得到元素的原始坐标。

Geo指令的基本使用

添加
geoadd指令携带集合名称以及多个经纬度名称三元组

127.0.0.1:6379> geoadd company 116.48105 39.996794 juejin 
(integer) 1
127.0.0.1:6379> geoadd company 116.514203 39.905409 ireader 
(integer) 1
127.0.0.1:6379> geoadd company 116.489033 40.007669 meituan
(integer) 1
127.0.0.1:6379> geoadd company 116.562108 39.787602 jd 116.334255 40.027400 xiaomi 
(integer) 2

计算距离

127.0.0.1:6379> geodist company juejin ireader km 
"10.5501"
127.0.0.1:6379> geodist company juejin meituan km 
"1.3878"
127.0.0.1:6379> geodist company juejin jd km 
"24.2739"
127.0.0.1:6379> geodist company juejin xiaomi km 
"12.9606"
127.0.0.1:6379> geodist company juejin juejin km 
"0.0000"

我们可以看到掘金离美团最近,因为它们都在望京。距离单位可以是 m、km、ml、ft, 分别代表米、千米、英里和尺。

获取元素位置

geopos指令可以获取集合中任意元素的经纬度坐标,可以一次获取多个。

127.0.0.1:6379> geopos company juejin 
"116.48104995489120483"
"39.99679348858259686"
127.0.0.1:6379> geopos company ireader 
"116.5142020583152771"
"39.90540918662494363" 
127.0.0.1:6379> geopos company juejin ireader 
"116.48104995489120483"
"39.99679348858259686""116.5142020583152771"
"39.90540918662494363"

观察到获取的经纬度坐标和getadd进去的坐标有轻微的误差,原因是geohash对二维坐标进行一维映射是有损的,通过映射在还原回来的值会出现较小的差别,对于【附近的人】来说,这种误差是可以接受的。

获取附近的公司
georadiusbymember指令是最为关键的指令,可以用来查询指定元素附近的其他元素

# 范围 20 公里以内最多 3 个元素按距离正排,它不会排除自身
127.0.0.1:6379> georadiusbymember company ireader 20 km count 3 asc 
1) "ireader"
2) "juejin"
3) "meituan"
# 范围 20 公里以内最多 3 个元素按距离倒排
127.0.0.1:6379> georadiusbymember company ireader 20 km count 3 desc 
1) "jd"
2) "meituan"
3) "juejin"
# 三个可选参数 withcoord withdist withhash 用来携带附加参数
# withdist 很有用,它可以用来显示距离
# withcoord 返回结果时包括地理位置的经度和纬度坐标
# withdist:返回结果时包括结果与指定地理位置之间的距离
# withhash:返回结果时包括地理位置的 geohash 值
127.0.0.1:6379> georadiusbymember company ireader 20 km withcoord withdist withhash count 3 asc1) 1) "ireader"2) "0.0000"3) (integer) 4069886008361398 4) 1) "116.5142020583152771"2) "39.90540918662494363"
2) 1) "juejin"2) "10.5501"3) (integer) 4069887154388167 4) 1) "116.48104995489120483"2) "39.99679348858259686" 
3) 1) "meituan"2) "11.5748"3) (integer) 4069887179083478 4) 1) "116.48903220891952515"2) "40.00766997707732031"

除了georadiusbymember指令根据元素查询附近的元素,Redis还提供了根据坐标值来查询附近的元素,这个指令更加有用。他可以根据用户的定位来计算【附近的车】,【附近的餐馆】等。他的参数和georadiusbymember基本一致,除了将目标元素改成经纬度坐标值。

127.0.0.1:6379> georadius company 116.514202 39.905409 20 km withdist count 3 asc 
1) 1) "ireader"2) "0.0000" 
2) 1) "juejin"2) "10.5501" 
3) 1) "meituan"2) "11.5748"

总结

在一个地图应用中,车的数据、餐馆的数据、人的数据可能会有百万千万条,如果使用Redis的Geo数据结构,他们将全部放在一个zset集合中,在Redis的集群环境中,集合可能会从一个节点迁移到另一个节点,如果单个key的数据过大,会对集群的迁移工作造成较大的影响,在集群环境中单个key对应的数据量不宜超过1Mb,否则会导致集群迁移出现卡顿现象,影响线上业务正常运行。

所以,建议Geo的数据使用单独的Redis实例部署,不使用集群环境。

如果数据量过亿甚至更大,就需要对Geo数据进行拆分,按国家拆分、按省拆分、按市拆分,在人口特大城市甚至可以按区拆分。这样就可以显著降低单个zset集合的大小。

相关文章:

Redis 地理散列GeoHash

用数据库来算附近的人 地图元素的位置数据使用二维的经纬度表示&#xff0c;经度范围&#xff08;-180&#xff0c;180]&#xff0c;纬度范围 &#xff08;-90&#xff0c;90],纬度正负以赤道为界&#xff0c;北正南负&#xff0c;经度正负已本初子午线&#xff08;英国格林尼…...

vim 显示行号

在 Vim 中&#xff0c;你可以通过几种不同的方式来显示行号。以下是两种常用的方法&#xff1a; 临时显示行号&#xff1a; 当你打开 Vim 并想要临时查看文件的行号时&#xff0c;你可以使用 :set number 命令。这个命令会在当前 Vim 会话中显示行号。如果你想要关闭行号显示&a…...

C++:调整数组顺序使奇数位于偶数前面【面试】

在C&#xff0c;如果要调整数组顺序使所有奇数位于偶数前面&#xff0c;这里提供一种简单且常用的方法&#xff1a;双指针技术。这种方法不需要额外的空间&#xff0c;并且时间复杂度为O(n)。 以下是使用双指针技术实现的示例代码&#xff1a; #include <iostream> #in…...

WPF/C#:程序关闭的三种模式

ShutdownMode枚举类型介绍 ShutdownMode是一个枚举类型&#xff0c;它定义了WPF应用程序的关闭方式。这个枚举类型有三个成员&#xff1a; OnLastWindowClose&#xff1a;当最后一个窗口关闭或者调用System.Windows.Application.Shutdown方法时&#xff0c;应用程序会关闭。O…...

登录/注册- 滑动拼图验证码(IOS/Swift)

本章介绍如何使用ios开发出滑动拼图验证码&#xff0c;分别OC代码和swift代码调用 1.导入项目model文件OC代码&#xff08;下载完整Demo&#xff09; 2.放入你需要显示的图片 一&#xff1a;OC调用 #import "ViewController.h" #import "CodeView.h"…...

MyBatis进行模糊查询时SQL语句拼接引起的异常问题

项目场景&#xff1a; CRM项目&#xff0c;本文遇到的问题是在实现根据页面表单中输入条件&#xff0c;在数据库中分页模糊查询数据&#xff0c;并在页面分页显示的功能时&#xff0c;出现的“诡异”bug。 开发环境如下&#xff1a; 操作系统&#xff1a;Windows11 Java&#…...

网站调用Edge浏览器API:https://api-edge.cognitive.microsofttranslator.com/translate

Edge浏览器有自带的翻译功能&#xff0c;在运行pc项目可能会遇到疯狂调用Edge的API https://api-edge.cognitive.microsofttranslator.com/translate 这个URL&#xff08;https://api-edge.cognitive.microsofttranslator.com/translate&#xff09;指向的是微软服务中的API接…...

css实现优惠券样式

实现优惠券效果&#xff1a; 实现思路&#xff1a; 需要三个盒子元素&#xff0c;使用 css 剪裁&#xff0c;利用 ellipse 属性&#xff0c;将两个盒子分别裁剪成两个半圆&#xff0c;位置固定在另一个盒子元素左右两边适当位置上。为另一个盒子设置想要的样式&#xff0c;圆角…...

函数递归(C语言)(详细过程!)

函数递归 一. 递归是什么1.1 递归的思想1.2 递归的限制条件 二. 递归举例2.1 求n的阶乘2.2 按顺序打印一个整数的每一位 三. 递归与迭代3.1 求第n个斐波那契数 一. 递归是什么 递归是学习C语言很重要的一个知识&#xff0c;递归就是函数自己调用自己&#xff0c;是一种解决问题…...

uniapp 接口请求封装

根目录下创建 config目录 api.js request.js // request.js // 封装一个通用的网络请求函数 适当调整 function httpRequest(options) {const userToken uni.getStorageSync(access_token).token;return new Promise((resolve, reject) > {uni.request({url: ${options.ur…...

C++中的观察者模式

目录 观察者模式&#xff08;Observer Pattern&#xff09; 实际应用 股票价格监控系统 发布-订阅系统 总结 观察者模式&#xff08;Observer Pattern&#xff09; 观察者模式是一种行为型设计模式&#xff0c;它定义了对象间的一对多依赖关系。当一个对象的状态发生改变…...

conda虚拟环境,安装pytorch cuda cudnn版本一致,最简单方式

1、pytorch版本安装&#xff08;卸载也会有问题&#xff09; &#xff08;1&#xff09;版本如何选择参考和卸载 https://zhuanlan.zhihu.com/p/401931724 &#xff08;2&#xff09;对应版本如何安装命令 https://pytorch.org/get-started/previous-versions/ 最简答安装参考…...

第 5 章:面向生产的 Spring Boot

在 4.1.2 节中&#xff0c;我们介绍了 Spring Boot 的四大核心组成部分&#xff0c;第 4 章主要介绍了其中的起步依赖与自动配置&#xff0c;本章将重点介绍 Spring Boot Actuator&#xff0c;包括如何通过 Actuator 提供的各种端点&#xff08;endpoint&#xff09;了解系统的…...

在 Windows 操作系统中,可以通过命令行工具来杀死进程

1. 使用 taskkill 命令 taskkill 命令是一个用于终止进程的命令行工具&#xff0c;可以通过进程名称或进程 ID (PID) 来杀死进程。 按进程名称杀死进程 taskkill /IM processname.exe /Fprocessname.exe 是进程的名称。/F 参数表示强制终止进程。 例如&#xff0c;终止名为…...

uni-app文件下载 h5 xls 乱码 锟斤拷 Blob pdf打不开

原先下载方式&#xff0c;PC管理端和浏览器打开文件能下载&#xff0c;xls没出现乱码&#xff0c;pdf能正常显示,H5下载xls乱码锟斤拷&#xff0c;PDF显示空白内容 怀疑是前端问题&#xff0c;也尝试过修改后端代码 后端设置编码格式 response.setCharacterEncoding(characte…...

Vue25-内置指令02:v-text指令

一、v-html对比v-text v-html支持结构的解析&#xff0c;v-text不支持结构的解析。 二、v-html的安全性问题 2-1、cookie的原理&#xff08;node.js&#xff09; 7天免登录&#xff0c;cookie实现。 cookie的本质就是类似于json的字符串&#xff0c;格式是&#xff1a;key-va…...

stable diffusion中的negative prompt是如何工作的

https://stable-diffusion-art.com/how-negative-prompt-work/https://stable-diffusion-art.com/how-negative-prompt-work/https://zhuanlan.zhihu.com/p/644879268...

STM32项目分享:智能小区充电桩系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB板打样焊接图 五、程序设计 六、实验效果 七、资料内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.c…...

PDU模块中浪涌保护模块与空开模块的应用

由于PDU具体应用的特殊性&#xff0c;其在规划设计时具有应用场景的针对性&#xff0c;同时PDU的高度定制化的特点&#xff0c;是其他电气联接与保护产品所不具备的。 PDU基础的输出输入功能外&#xff0c;其电路的控制与电压保护器同时也极为重要。空气开关和浪涌保护器相关功…...

19、Go Gin框架集成Swagger

介绍&#xff1a; Swagger 支持在 Gin 路由中使用一系列注释来描述 API 的各个方面。以下是一些常用的 Swagger 注释属性&#xff0c;这些属性可以在 Gin 路由的注释中使用&#xff1a; Summary: 路由的简短摘要。Description: 路由的详细描述。Tags: 用于对路由进行分类的标…...

自动同步库数据——kettle开发36

kettle中的那些人工智能。 一、kettle的AI能力目录 跨库同步 2.自动开发 3.自动优化 二、AI实例 1、跨库同步 sqlsever表同步至oracle数据库 1.1源库sqlserver 1.2目标库oracle 1.3可视化跨库同步 使用多表复制向导 选择跨库的表&#xff0c;下一步下一步&#xff0c;即可…...

MSOCache在电脑中可以删除吗?

MSOCache文件夹在电脑中是可以删除的。但删除前需要了解以下几点&#xff1a; MSOCache文件夹的作用&#xff1a; MSOCache文件夹是Microsoft Office的本地安装源&#xff0c;用于存储Office安装和更新过程中所需的临时文件&#xff0c;如安装程序所需的组件、配置设置以及更新…...

数据网格和视图入门

WinForms数据网格&#xff08;GridControl类&#xff09;是一个数据感知控件&#xff0c;可以以各种格式&#xff08;视图&#xff09;显示数据。本主题包含以下部分&#xff0c;这些部分将指导您如何使用网格控件及其视图和列&#xff08;字段&#xff09;。 Grid Control’s…...

雨的轮回与生命的律动

雨的轮回与生命的律动 我们生活在一个充满变数的世界里&#xff0c;许多事情无法预测&#xff0c;如同这不知何时会停歇的雨。然而&#xff0c;尽管我们无法预知雨停的确切时刻&#xff0c;但我们深知&#xff0c;这场雨终将会过去&#xff0c;阳光终将再次洒满大地。这种对未…...

CANopen for Python 使用教程(二)

系列文章目录 前言 CANopen 标准的 Python 实现。该项目的目的是在一个简单的 Pythonic 接口中支持 CiA 301 标准中最常见的部分。它主要针对测试和自动化任务&#xff0c;而不是符合标准的主实施。 该库支持 Python 3.6 及以上版本。 一、特点 该库主要用作主库。 NMT 主站…...

前方碰撞缓解系统技术规范(简化版)

前方碰撞缓解系统技术规范(简化版) 1 系统概述2 工作时序3 预警目标4 功能条件5 HMI开关6 显示需求7 相关子功能8 TTC标定参考9 指标需求1 系统概述 前方碰撞缓解系统包含LW潜在危险报警、FCW前方碰撞预警和AEB自动紧急制动三个部分。 LW潜在危险报警:根据本车与前车保持的…...

数据赋能(117)——体系:数据收集——实施过程、应用特点

实施过程 数据收集过程是一个系统化、有序的步骤集合&#xff0c;旨在确保能够准确、高效地获取所需数据。以下是数据收集过程的基本步骤&#xff1a; 明确数据需求&#xff1a;这是数据收集的第一步&#xff0c;需要明确需要收集哪些类型的数据&#xff0c;这些数据将如何支…...

【吃包子game】

如果您想要编写一个简单的“吃包子”游戏代码&#xff0c;可以使用Python语言来实现。下面是一个简单的例子&#xff0c;该游戏会随机生成一定数量的包子&#xff0c;玩家每次可以吃掉一个包子&#xff0c;直到包子被吃光为止。 import random def eat_dumplings():# 随机生成…...

图片转Base64

在Python中, 可以使用内置的base64模块以及图像处理库(如PIL, 也称为Pillow)来将图片转换为Base64编码的字符串. 以下是一个简单的示例, 说明如何实现这一过程:首先, 需要安装Pillow库(如果尚未安装), 可以使用pip来安装: pip install pillow然后, 可以使用以下Python代码将图片…...

STM32项目分享:智能家居语音系统

目录 一、前言 二、项目简介 1.功能详解 2.主要器件 三、原理图设计 四、PCB硬件设计 1.PCB图 2.PCB打板焊接图: 五、程序设计 六、实验效果 七、包含内容 项目分享 一、前言 项目成品图片&#xff1a; 哔哩哔哩视频链接&#xff1a; https://www.bilibili.com…...

用百度地图 做gis网站/无锡网络推广外包

效果图&#xff1a; 有的时候&#xff0c;在图片或者标签的左&#xff08;右&#xff09;上角展示一个角标。那么该如何实现呢&#xff1f; 思路&#xff1a; 图片包裹在一个容器里&#xff0c;且这个容器有定位属性便于后代元素进行定位&#xff0c;溢出部分隐藏&#xff08…...

做网站要注意什么/长春seo按天计费

看了前面一节&#xff1a;linux shell数据重定向(输入重定向与输出重定向)详细分析 估计还有一些朋友是头晕晕的&#xff0c;好复杂的重定向了。这次我们看下管道命令了。shell管道&#xff0c;可以说用法就简单多了。管道命令操作符是&#xff1a;”|”,它仅能处理经由前面一个…...

织梦做旅游网站/郑州网站设计

微信小程序如何上传、提交审核、发布操作 工具/原料 微信开发工具 小程序 方法/步骤 首先&#xff0c;账号登陆微信公众号后台进行设置小程序基本信息设置操作 登陆后&#xff0c;设置小程序基本信息&#xff0c;包括名称、头像、介绍及服务范围&#xff0c;开发设置中的服务…...

做网站分辨率设置多少/网站自助建站系统

每个具体的对象后面都隐藏着抽象的类。 flash 中as3.0中所有的类&#xff0c;都是为了创建对象所用的。反过来&#xff0c;所创建的任何具体对象都隐藏着抽象的类。 类可以把它看做函数&#xff0c;类的属性是函数的数据&#xff0c;类的方法是函数的语句代码。转载于:https://…...

旅游高端网站建设/搜索引擎优化方法案例

转自&#xff1a;https://www.cnblogs.com/lbole/p/8904298.html 一、硬盘接口类型 硬盘的接口主要有IDE、SATA、SCSI 、SAS和光纤通道等五种类型。其中IDE和SATA接口硬盘多用于家用产品中&#xff0c;也有部分应用于服务器&#xff0c;SATA是一种新生的硬盘接口类型&#xff0…...

wordpress站群管理/百度推广怎么样才有效果

通过万岁&#xff01;&#xff01;&#xff01; 题目&#xff1a;给你一个数n&#xff0c;然后问他的二进制中有多少个1。思路&#xff1a;我们让这个二进制分别跟2的i次方进行与运算&#xff0c;如果这个值不是0&#xff0c;则表示第i位是1&#xff0c;这时候我们让返回结果1…...