欢迎大家前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~

随着国内服务共享化的热潮普及,共享单车,共享雨伞,共享充电宝等各种服务如雨后春笋,随之而来的LBS服务定位问题成为了后端服务的一个挑战。MongoDB对LBS查询的支持较为友好,也是各大LBS服务商的首选数据库。腾讯云MongoDB团队在运营中发现,原生MongoDB在LBS服务场景下有较大的性能瓶颈,经腾讯云团队专业的定位分析与优化后,云MongoDB在LBS服务的综合性能上,有10倍以上的提升。
腾讯云MongoDB提供的优异综合性能,为国内各大LBS服务商,例如摩拜单车等,提供了强有力的保障。

LBS业务特点

以共享单车服务为例,LBS业务具有2个特点,分别是时间周期性和坐标分布不均匀。

一.时间周期性

高峰期与低谷期的QPS量相差明显,并且高峰期和低峰期的时间点相对固定。

二.坐标分布不均匀

坐地铁的上班族,如果留意可能会发现,在上班早高峰时,地铁周围摆满了共享单车,而下班 时段,地铁周围的共享单车数量非常少。如下图,经纬度(121,31.44)附近集中了99%以上 的坐标。此外,一些特殊事件也会造成点的分布不均匀,例如深圳湾公园在特殊家假日涌入大量的客户,同时这个地域也会投放大量的单车。部分地域单车量非常集中,而其他地域就非常稀疏。

MongoDB的LBS服务原理

MongoDB中使用2d_index 或2d_sphere_index来创建地理位置索引(geoIndex),两者差别不大,下面我们以2d_index为例来介绍。

一.2D索引的创建与使用

db.coll.createIndex({"lag":"2d"}, {"bits":int}))
通过上述命令来创建一个2d索引,索引的精度通过bits来指定,bits越大,索引的精度就越高。更大的bits带来的插入的overhead可以忽略不计
db.runCommand({
geoNear: tableName,
maxDistance: 0.0001567855942887398,
distanceMultiplier: 6378137.0,
num: ,
near: [ 113.8679388183982, 22.58905429302385 ],
spherical: true|false})

通过上述命令来查询一个索引,其中spherical:true|false 表示应该如何理解创建的2d索引,false表示将索引理解为平面2d索引,true表示将索引理解为球面经纬度索引。这一点比较有意思,一个2d索引可以表达两种含义,而不同的含义是在查询时被理解的,而不是在索引创建时。

二.2D索引的理论

MongoDB 使用GeoHash的技术来构建2d索引(见wiki geohash 文字链 https://en.wikipedia.org/wiki/Geohash )。MongoDB使用平面四叉树划分的方式来生成GeoHashId,每条记录有一个GeoHashId,通过GeoHashId->RecordId的索引映射方式存储在Btree中。

很显然的,一个2bits的精度能把平面分为4个grid,一个4bits的精度能把平面分为16个grid。
2d索引的默认精度是长宽各为26,索引把地球分为(226)(226)块,每一块的边长估算为2PI6371000/(1<<26) = 0.57 米。
mongodb的官网上说的60cm的精度就是这么估算出来的。
By default, a 2d index on legacy coordinate pairs uses 26 bits of precision, which isroughly 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的查询过程,查询语句如下

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在实现的细节中,如果内环搜索到的点数过少,圆环每次扩张的步长会倍增

MongoDB LBS服务遇到的问题

部分大客户在使用MongoDB的geoNear功能查找附近的对象时,经常会发生慢查询较多的问题,早高峰压力是低谷时段的10-20倍,坐标不均匀的情况慢查询严重,濒临雪崩。初步分析发现,这些查询扫描了过多的点集。

如下图,查找500米范围内,距离最近的10条记录,花费了500ms,扫描了24000+的记录。类似的慢查询占据了高峰期5%左右的查询量

测试环境复现与定位

排查数据库的性能问题,主要从锁等待,IO等待,CPU消耗三封面分析。上面的截图扫描了过多的记录,直觉上是CPU或者IO消耗性的瓶颈。为了严谨起见,我们在测试环境复现后,发现慢日志中无明显的timeAcquiringMicroseconds项排除了MongoDB执行层面的锁竞争问题,并选用较大内存的机器使得数据常驻内存,发现上述用例依旧需要200ms以上的执行时间。10核的CPU资源针对截图中的case,只能支持50QPS。

为何扫描集如此大

上面我们说过,MongoDB搜索距离最近的点的过程是一个环形扩张的过程,如果内环满足条件的点不够多,每次的扩张半径都会倍增。因此在遇到内环点稀少,外环有密集点的场景时,容易陷入BadCase。如下图,我们希望找到离中心点距离最近的三个点。由于圆环扩张太快,外环做了很多的无用扫描与排序。

这样的用例很符合实际场景,早高峰车辆聚集在地铁周围,你从家出发一路向地铁,边走边找,共享单车软件上动态搜索距你最近的10辆车,附近只有三两辆,于是扩大搜索半径到地铁周围,将地铁周围的所有几千辆车都扫描计算一遍,返回距离你最近的其余的七八辆。

问题的解决

问题我们已经知道了,我们对此的优化方式是控制每一圈的搜索量,为此我们为geoNear命令增加了两个参数,将其传入NearStage中。hintCorrectNum可以控制结果品质的下限,返回的前N个一定是最靠近中心点的N个点。hintScan用以控制扫描集的大小的上限。

hintScan: 已经扫描的点集大小大于hintScan后,做模糊处理。
hintCorrectNum:已经返回的结果数大于hintCorrectNum后,做模糊处理。

该优化本质上是通过牺牲品质来尽快返回结果。对于国内大部分LBS服务来说,完全的严格最近并不是必要的。且可以通过控制参数获得严格最近的效果。在搜索过程中,密集的点落到一个环内,本身距离相差也不会不大。该优化在上线后,将部分大客户的MongoDB性能上限从单机1000QPS提升了10倍到10000QPS以上。

和Redis3.2的对比

Redis3.2也加入了地理位置查询的功能,我们也将开源Redis和云数据库MongoDB进行对比。
Redis使用方式:GEORADIUS appname 120.993965 31.449034 500 m count 30 asc。在密集数据集场景下,使用腾讯云MongoDB和开源的Redis进行了性能对比。下图是在密集数据集上,在24核CPU机器上,MongoDB单实例与Redis单实例的测试对比。需要注意的是Redis本身是单线程的内存缓存数据库。MongoDB是多线程的高可用持久化的数据库,两者的使用场景有较大不同。

总结

MongoDB原生的geoNear接口是国内各大LBS应用的主流选择。原生MongoDB在点集稠密的情况下,geoNear接口效率会急剧下降,单机甚至不到1000QPS。腾讯云MongoDB团队对此进行了持续的优化,在不影响效果的前提下,geoNear的效率有10倍以上的提升,为我们的客户如摩拜提供了强力的支持,同时相比Redis3.2也有较大的性能优势。

推荐阅读

邹方明:看腾讯云如何架构海量存储系统
熊普江: BGP网络架构助力开发者快速构建、优化业务
唐良:云端架构给电商行业带来创新力
黄荣奎:如何快速、便捷开发小程序
电商行业开发者如何基于云端构建业务?腾讯云+未来峰会上这样说


此文已由作者授权腾讯云技术社区发布,转载请注明文章出处
原文链接:https://cloud.tencent.com/community/article/723875

云 MongoDB 优化让 LBS 服务性能提升十倍的更多相关文章

  1. 必看!如何让你的LBS服务性能提升十倍!

    本文由云+社区发表 作者:腾讯云数据库团队 随着国内服务共享化的热潮普及,共享单车,共享雨伞,共享充电宝等各种服务如雨后春笋,随之而来的LBS服务定位问题成为了后端服务的一个挑战.MongoDB对LB ...

  2. Web 应用性能提升 10 倍的 10 个建议

    转载自http://blog.jobbole.com/94962/ 提升 Web 应用的性能变得越来越重要.线上经济活动的份额持续增长,当前发达世界中 5 % 的经济发生在互联网上(查看下面资源的统计 ...

  3. 重构、插件化、性能提升 20 倍,Apache DolphinScheduler 2.0 alpha 发布亮点太多!

    点击上方 蓝字关注我们 社区的小伙伴们,好消息!经过 100 多位社区贡献者近 10 个月的共同努力,我们很高兴地宣布 Apache DolphinScheduler 2.0 alpha 发布.这是 ...

  4. 如何将 iOS 工程打包速度提升十倍以上

    如何将 iOS 工程打包速度提升十倍以上   过慢的编译速度有非常明显的副作用.一方面,程序员在等待打包的过程中可能会分心,比如刷刷朋友圈,看条新闻等等.这种认知上下文的切换会带来很多隐形的时间浪费. ...

  5. 百万级高并发mongodb集群性能数十倍提升优化实践

    背景 线上某集群峰值TPS超过100万/秒左右(主要为写流量,读流量很低),峰值tps几乎已经到达集群上限,同时平均时延也超过100ms,随着读写流量的进一步增加,时延抖动严重影响业务可用性.该集群采 ...

  6. 腾讯 TKE 厉害了!用 eBPF绕过 conntrack 优化K8s Service,性能提升40%

    Kubernetes Service[1] 用于实现集群中业务之间的互相调用和负载均衡,目前社区的实现主要有userspace,iptables和IPVS三种模式.IPVS模式的性能最好,但依然有优化 ...

  7. 在MongoDB中创建一个索引而性能提升1000倍的小例子

    在https://www.cnblogs.com/xuliuzai/p/9965229.html的博文中我们介绍了MongoDB的常见索引的创建语法.部分同学还想看看MongoDB的威力到底有多大,所 ...

  8. 有史以来性价比最高最让人感动的一次数据库&amp;SQL优化(DB &amp; SQL TUNING)——半小时性能提升千倍

    昨天,一个客户现场人员急急忙忙打电话找我,说需要帮忙调优系统,因为经常给他们干活,所以,也就没多说什么,先了解情况,据他们说,就是他们的系统最近才出现了明显的反应迟钝问题,他们的那个系统我很了解,软硬 ...

  9. 存算分离下写性能提升10倍以上,EMR Spark引擎是如何做到的?

    ​引言 随着大数据技术架构的演进,存储与计算分离的架构能更好的满足用户对降低数据存储成本,按需调度计算资源的诉求,正在成为越来越多人的选择.相较 HDFS,数据存储在对象存储上可以节约存储成本,但与此 ...

随机推荐

  1. 浅谈CSS模块化

    为什么要CSS模块化? 你是否为class命名而感到苦恼? 你是否有怕跟别人使用同样class名而感到担忧? 你是否因层级结构不清晰而感到烦躁? 你是否因代码难以复用而感到不爽? 你是否因为commo ...

  2. 监听 window.open 打开的窗口关闭并回调

    第三方的登录的解决方案通常有两种方式,一是打开一个新的标签页,然后登录回调回来: 二是通过父窗口打开一个子窗体去第三方登录,登陆成功时关掉子窗体回到父窗口. 问题来了 我的父窗体怎么样才知道子窗体被关 ...

  3. (转载)公开的海量数据集 Public Research-Quality Datasets

    转载自:http://rensanning.iteye.com/blog/1601663 海量数据数据集 海量数据(又称大数据)已经成为各大互联网企业面临的最大问题,如何处理海量数据,提供更好的解决方 ...

  4. Android 音视频开发(二):使用 AudioRecord 采集音频数据并保存到文件

    版权声明:转载请说明出处:http://www.cnblogs.com/renhui/p/7457321.html 一.AudioRecord API详解 AudioRecord是Android系统提 ...

  5. GUI与UI的区别

    GUI 图形用户界面(**Graphical User Interface**,简称 GUI,又称图形用户接口)是指采用图形方式显示的计算机操作用户界面. 与早期计算机使用的命令行界面相比,图形界面对 ...

  6. jQuery圣诞雪花

    <script type="text/javascript"> $(function(){ var d="<div class='y_snow'> ...

  7. mysql数据库优化之开启慢查询日志

    进入mysql数据库,使用 show variables like 'slow_query_log'; 查看是否开启了慢查询日志 value值为OFF,则慢查询日志没有开启,在开启慢查询日志之前,我们 ...

  8. 斐波那契数列—Java

    斐波那契数列想必大家都知道吧,如果不知道的话,我就再啰嗦一遍, 斐波那契数列为:1 2 3 5 8 13 ...,也就是除了第一项和第二项为1以外,对于第N项,有f(N)=f(N-1)+f(N-2). ...

  9. Android WebView基本使用

    转载请注明出处: http://blog.csdn.net/lowprofile_coding/article/details/77928614 WebView介绍 Android WebView在A ...

  10. C# 导出数据到Excel模板中(转)

    今天做报表的时候遇到了多表头的问题,而且相应的报表的格式都一样.所以就采用了报表模板的方式来进行. 第一步:在开发的当前项目中引入:Microsoft.Office.Interop.Excel:Sys ...