作者:孔德雨

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应用的存储方案。

相关阅读

MongoDB复制集原理

MongoDb Mmap引擎分析

基于用户画像大数据的电商防刷架构


此文已由作者授权腾讯云技术社区发布,转载请注明文章出处,获取更多云计算技术干货,可请前往腾讯云技术社区

原文链接:https://www.qcloud.com/community/article/36629001491016578

欢迎大家关注腾讯云技术社区-博客园官方主页,我们将持续在博客园为大家推荐技术精品文章哦~

Mongodb Geo2d索引原理的更多相关文章

  1. mongodb的索引原理

    首先说一下为什么要有索引,大家都知道mongdb是非关系型文档类型数据库,用过的人都有同一种感受,查询的效率太低,当你想提高查询效率的时候可以就需要使用索引了. 哈哈,本来想写一篇的,在网上看到了一篇 ...

  2. 图解 MongoDB 地理位置索引的实现原理(转)

    原文链接:图解 MongoDB 地理位置索引的实现原理 地理位置索引支持是MongoDB的一大亮点,这也是全球最流行的LBS服务foursquare 选择MongoDB的原因之一.我们知道,通常的数据 ...

  3. MongoDB · 引擎特性 · MongoDB索引原理

    MongoDB · 引擎特性 · MongoDB索引原理数据库内核月报原文链接 http://mysql.taobao.org/monthly/2018/09/06/ 为什么需要索引?当你抱怨Mong ...

  4. MongoDB索引(一) --- 入门篇:学习使用MongoDB数据库索引

    这个系列文章会分为两篇来写: 第一篇:入门篇,学习使用MongoDB数据库索引 第二篇:进阶篇,研究数据库索引原理--B/B+树的基本原理 1. 准备工作 在学习使用MongoDB数据库索引之前,有一 ...

  5. MongoDB之索引

    索引是用来加快查询的,这里不解说索引的原理和数据结构.事实上大部分数据库的索引就是B+Tree,想要了解的同学能够看索引原理,要掌握怎样为查询配置最佳索引会有些难度. MongoDB索引差点儿和关系型 ...

  6. MongoDB 分片的原理、搭建、应用 !

    MongoDB 分片的原理.搭建.应用   一.概念: 分片(sharding)是指将数据库拆分,将其分散在不同的机器上的过程.将数据分散到不同的机器上,不需要功能强大的服务器就可以存储更多的数据和处 ...

  7. mongodb基本命令,mongodb集群原理分析

    mongodb基本命令,mongodb集群原理分析 集合: 1.集合没有固定数据格式. 2. 数据: 时间类型: Date() 当前时间(js时间) new Date() 格林尼治时间(object) ...

  8. MySQL的InnoDB索引原理详解

    摘要 本篇介绍下Mysql的InnoDB索引相关知识,从各种树到索引原理到存储的细节. InnoDB是Mysql的默认存储引擎(Mysql5.5.5之前是MyISAM,文档).本着高效学习的目的,本篇 ...

  9. MySQL索引原理及慢查询优化

    原文:http://tech.meituan.com/mysql-index.html 一个慢查询引发的思考 select count(*) from task where status=2 and ...

随机推荐

  1. BZOJ 1877: [SDOI2009]晨跑(费用流)

    看到要求两个量就下意识的想到了费用流= =,先把一个点拆成两个点就能够解决一个的只经过一次的限制 CODE: #include<cstdio>#include<iostream> ...

  2. C++_运算符重载

    什么是运算符的重载? 运算符与类结合,产生新的含义. 为什么要引入运算符重载? 作用:为了实现类的多态性(多态是指一个函数名有多种含义) 怎么实现运算符的重载? 方式:类的成员函数 或 友元函数(类外 ...

  3. 智能打印SDK---官方博客

    (开源准备:开源为百度云打包开源,GIT太慢,SVN没有免费的,TFS有免费的也慢的要命) 开源地址:http://pan.baidu.com/s/1miobLoO 电子商务越来越成熟,提供的服务越来 ...

  4. css4激动人心的新特性及浏览器支持度

    CSS3的选择器提供了很多像:nth-child这样有用的选择器,并且得到浏览器支持.CSS的第四代 选择器CSS4选择器),经我们带来了更多有用的选择器. 1.否定伪类:not 否定伪类选择器其实在 ...

  5. zhenai

    1.2=http://files.cnblogs.com/files/bqh10086/zhenai_1.2_pack.zip

  6. Foundation Kit介绍

    Cocoa实际上是由许多个不同的框架组成的,其中最常用于桌面端(OS X)应用程序的是Foundation和Application Kit.它包含了所有的用户界面对象和高级类.如果打算开发ios平台上 ...

  7. 第22篇 js中的this指针的用法

    前面把js的相关知识总结了下,今天把js中的上下文的this,对于强类型语言,this的用法非常的单一,因为他们没有js特有的动态绑定. 首先看下面代码: function funcA() { thi ...

  8. 前端性能监控:window.performance

    window.performance 是W3C性能小组引入的新的API,目前IE9以上的浏览器都支持.一个performance对象的完整结构如下图所示: memory字段代表JavaScript对内 ...

  9. appium desktop 版本发布

    Appium Desktop is an open source app for Mac, Windows, and Linux which gives you the power of the Ap ...

  10. Java面试11|Maven与Git

    git的命令一定要掌握,如果学习可以参考:廖雪峰的官方网站 1.Maven 生命周期及Maven多项目聚合与继承 Maven的生命周期分如下的9个阶段. (1)clean 清理自动生成的文件,也就是t ...