Mongodb Geo2d索引原理
作者:孔德雨
MongoDB的geo索引是其一大特色,本文从原理层面讲述geo索引中的2d索引的实现。
2d 索引的创建与使用
通过 db.coll.createIndex({"lag":"2d"}, {"bits":int}))
来创建一个2d索引,索引的精度通过bits来指定,bits越大,索引的精度就越高。更大的bits带来的插入的overhead可以忽略不计。
通过
db.runCommand({
geoNear: tableName,
maxDistance: 0.0001567855942887398,
distanceMultiplier: 6378137.0,
num: 30,
near: [ 113.8679388183982, 22.58905429302385 ],
spherical: true|false})
来查询一个索引,其中spherical:true|false 表示应该如何理解创建的2d索引,false表示将索引理解为平面2d索引,true表示将索引理解为球面经纬度索引。这一点比较有意思,一个2d索引可以表达两种含义,而不同的含义是在查询时被理解的,而不是在索引创建时。
2d索引的理论
Mongodb 使用一种叫做Geohash的技术来构建2d索引,但是Mongodb的Geohash并没有使用国际通用的每一层级32个grid的Geohash描述方式(见wiki geohash)。而是使用平面四叉树的形式。
如下图:
很显然的,一个2bits的精度能把平面分为4个grid,一个4bits的精度能把平面分为16个grid。2d索引的默认精度是长宽各为26,索引把地球分为(2^26)(2^26)块,每一块的边长估算为
2*PI*6371000/(1<<26) = 0.57 米
mongodb的官网上说的60cm的精度就是这么估算出来的:
By default, a 2d index on legacy coordinate pairs uses 26 bits of precision, which is roughly equivalent to 2 feet or 60 centimeters of precision using the default range of -180 to 180.
2d索引在Mongodb中的存储
上面我们讲到Mongodb使用平面四叉树的方式计算Geohash。事实上,平面四叉树仅存在于运算的过程中,在实际存储中并不会被使用到。
插入
对于一个经纬度坐标[x,y],MongoDb计算出该坐标在2d平面内的grid编号,该编号为是一个52bit的int64类型,该类型被用作btree的key,因此实际数据是按照 {GeoHashId->RecordValue}的方式被插入到btree中的。
查询
对于geo2D索引的查询,常用的有geoNear和geoWithin两种。geoNear查找距离某个点最近的N个点的坐标并返回,该需求可以说是构成了LBS服务的基础(陌陌,滴滴,摩拜), geoWithin是查询一个多边形内的所有点并返回。我们着重介绍使用最广泛的geoNear查询。
geoNear的查询过程
geoNear的查询语句如下:
db.runCommand(
{
geoNear: "places", //table Name
near: [ -73.9667, 40.78 ] , // central point
spherical: true, // treat the index as a spherical index
query: { category: "public" } // filters
maxDistance: 0.0001531 // distance in about one kilometer
}
)
geoNear可以理解为一个从起始点开始的不断向外扩散的环形搜索过程。如下图所示:
由于圆自身的性质,外环的任意点到圆心的距离一定大于内环任意点到圆心的距离,所以以圆环进行扩张迭代的好处是:
1)减少需要排序比较的点的个数
2)能够尽早发现满足条件的点从而返回,避免不必要的搜索
点集密度估算
那么,如何确定初始迭代步长呢,mongoDB认为初始迭代步长和点集密度相关。
geoNear 会根据点集的密度来确定迭代的初始步长。估算步骤如下:
1)从最小步长默认为60cm向外以矩形范围搜索,如果范围内有至少一个点,则停止搜索,转3)否则转 2)
2)步长倍增,继续步骤1)
3)以矩形对角线长度的三倍作为初始迭代步长。
圆环覆盖与索引前缀原理
上面我们说过,每一次的搜索都是以圆环为单位进行的,但是真实存入Btree中的是{GeoHashId->RecordValue},计算出与圆环相交的所有边长60cm的格子的GeoHash的值并在Btree中搜素绝对是一个非常愚蠢的做法,因为如果圆环的面积很大,光是枚举所有的GeoHash就有上百万个。
但是换个角度来看,其实以地球为一个整体去看待存储的点,绝对是稀疏的。这个稀疏的性质使得我们可以粗略的以平面四叉树的角度自上而下的找出与圆环相交的四叉树中间节点。
整个平面与圆环必然是相交的,于是将平面一分为四,剔除不相交的部分,对于每个留下来的子平面,继续一分为四,剔除不相交的部分,经过多轮迭代,留下来的子平面的GeoHash都是该子平面中所有grid的索引前缀,如下面四幅图所示:
上面四幅图中,分别为整个平面被四叉树划分0,1,2,3次后与圆环的相交情况,如果继续往下细分,所形成的图形就越来越逼近整个圆环。MongoDB中使用参数internalGeoNearQuery2DMaxCoveringCells来限制最多逼近到多少个子平面与圆环相交,默认为16。
我们注意到,上述平面划分过程为四叉树的分裂过程,每一次分裂都使得递归搜索的子平面与父平面有相同的GeoHash前缀(这里需要思考为什么,可能不太明显),因此每一个子平面可以对应于BTree中一段连续的Range(这里又是为什么?),也正因此,该参数越大,会使得需要搜索的子平面越少,但是会使得Btree的Range搜索更趋向于随机化搜索,导致更多的IO。我们知道Btree更适合于做Range搜索,所以对该参数的调整需要慎重。
展望
MongoDB原生的geoNear接口是国内各大LBS应用的主流选择。腾讯云的MongoDB专家经过测试发现,在点集稠密的情况下,MongoDB原生的geoNear接口效率会急剧下降,单机甚至不到1000QPS。腾讯云MongoDB对此进行了持续的优化,在不影响效果的前提下,geoNear的效率有10倍以上的提升,建议大家选择腾讯云MongoDB作为LBS应用的存储方案。
相关阅读
此文已由作者授权腾讯云技术社区发布,转载请注明文章出处,获取更多云计算技术干货,可请前往腾讯云技术社区
原文链接:https://www.qcloud.com/community/article/36629001491016578
欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~
Mongodb Geo2d索引原理的更多相关文章
- mongodb的索引原理
首先说一下为什么要有索引,大家都知道mongdb是非关系型文档类型数据库,用过的人都有同一种感受,查询的效率太低,当你想提高查询效率的时候可以就需要使用索引了. 哈哈,本来想写一篇的,在网上看到了一篇 ...
- 图解 MongoDB 地理位置索引的实现原理(转)
原文链接:图解 MongoDB 地理位置索引的实现原理 地理位置索引支持是MongoDB的一大亮点,这也是全球最流行的LBS服务foursquare 选择MongoDB的原因之一.我们知道,通常的数据 ...
- MongoDB · 引擎特性 · MongoDB索引原理
MongoDB · 引擎特性 · MongoDB索引原理数据库内核月报原文链接 http://mysql.taobao.org/monthly/2018/09/06/ 为什么需要索引?当你抱怨Mong ...
- MongoDB索引(一) --- 入门篇:学习使用MongoDB数据库索引
这个系列文章会分为两篇来写: 第一篇:入门篇,学习使用MongoDB数据库索引 第二篇:进阶篇,研究数据库索引原理--B/B+树的基本原理 1. 准备工作 在学习使用MongoDB数据库索引之前,有一 ...
- MongoDB之索引
索引是用来加快查询的,这里不解说索引的原理和数据结构.事实上大部分数据库的索引就是B+Tree,想要了解的同学能够看索引原理,要掌握怎样为查询配置最佳索引会有些难度. MongoDB索引差点儿和关系型 ...
- MongoDB 分片的原理、搭建、应用 !
MongoDB 分片的原理.搭建.应用 一.概念: 分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程.将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处 ...
- mongodb基本命令,mongodb集群原理分析
mongodb基本命令,mongodb集群原理分析 集合: 1.集合没有固定数据格式. 2. 数据: 时间类型: Date() 当前时间(js时间) new Date() 格林尼治时间(object) ...
- MySQL的InnoDB索引原理详解
摘要 本篇介绍下Mysql的InnoDB索引相关知识,从各种树到索引原理到存储的细节. InnoDB是Mysql的默认存储引擎(Mysql5.5.5之前是MyISAM,文档).本着高效学习的目的,本篇 ...
- MySQL索引原理及慢查询优化
原文:http://tech.meituan.com/mysql-index.html 一个慢查询引发的思考 select count(*) from task where status=2 and ...
随机推荐
- arguments及arguments.callee
首先有一个JavaScript函数 function test(a, b, c, d) { return a + b; } 在JavaScript中调用一个函数的实参个数可以和被调用函数的形参个数不匹 ...
- SQLHELPER 帮助类
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threa ...
- Kindle电子阅读器收不到个人文档推送解决方案
最近我的 kindle 固件版本更新到 5.8.7.0.1 ,发现增加了生字注音功能,瞬间变成小学生阅读神器有木有,不过,这个功能可以隐藏.显示,看着碍眼隐藏即可,还可以减少和增加生字注音.感觉对于经 ...
- 【疑问】css
1.p{float:left}好像意思是p后面的元素往左浮动的意思啊!!2.ff下,button的文字好像没法垂直居中2.1 button垂直对齐,其line-height似乎需要减2px(依边框宽度 ...
- 用smarty模板做数据实现修改、分页等功能
先来看怎么把数据库的列表全都显示出来 还是要先建一个php文件,还有html文件,都存到相应的目录下 php文件中 <?php include("../init.inc.php&quo ...
- 转:关于JSON.parse(),JSON.stringify(),jQuery.parseJSON()的用法
1. JSON.parse(jsonString): 在一个字符串中解析出JSON对象 ? 1 2 3 var str = '[{"href":"baidu.com&qu ...
- 基于 Koa平台Node.js开发的KoaHub.js的控制器,模型,帮助方法自动加载
koahub-loader koahub-loader是基于 Koa平台Node.js开发的KoaHub.js的koahub-loader控制器,模型,帮助方法自动加载 koahub loader I ...
- CoreAnimation 开篇
CoreAnimation 开篇 CoreAnimation系列博客是我对学习CoreAnimation的知识整理,博客排列顺序以及知识讲解存在欠缺望见谅. 博客的编写是在工作之余,尽量保证CoreA ...
- 3360: [Usaco2004 Jan]算二十四
3360: [Usaco2004 Jan]算二十四 Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 6 Solved: 6[Submit][Statu ...
- iOS多线程——同步异步串行并行
串行并行异步同步的概念很容易让人混淆,关于这几个概念我在第一篇GCD中有解释,但是还不够清晰,所以这里重写一篇博客专门对这几个概念进行区分: 先说一下队列和任务: (1)队列分为串行和并行,任务的执行 ...