LBS空间搜索架构的优化历程  
尹非凡@美团配送  20160925  
2
关于.  我  
2009.01 2011.01 2015.11   现在  
MIM  Software   爱奇艺   新美大  
    
云平台  
  
1:云推送  
2:Passport  
3:爱奇艺泡泡  
技术研发部  
  
1:医疗软件开发  
配送事业部  
  
1:LBS  
2:动态定价  
3:动态预测  
3
目录  
l  关于  .  我  
l  LBS与O2O  
l  主要业务场景  
l  LBS服务演化  
l  MongoDB方案  
l  MySQL+GeoHash方案  
l  Redis+GeoHash方案  
4
LBS与O2O  
LBS  
团购  
打车  
外卖配送  
餐饮   商超  
电影票  
5
配  
送  
系  
统  
如何将用户的订单交给合适的骑手配送?  
主要业务场景  
商家在哪里?  
入驻时确定  
坐标固定  
用户在哪里?  
下单时确定  
坐标固定  
骑手在哪里?  
实时变化  
坐标不固定  
6
主要业务场景  
骑手实时坐标上传  
骑手多,上报QPS高  
峰值QPS:1.5W+  
7
主要业务场景
订单找  
骑手  派单  
推单  
抢单  
人工派单  
订单找  
骑手  
8
主要业务场景  
空间搜索  
订单找骑手   骑手找订单  
9
业内解决方案  
更新索引效率低  
不适合实时更新场景  
GeoHash  
笛卡尔  
层级  
R树  
MongoDB  
  Postgre/PostGIS  
Lucene/Solr/ES  
10
当配送小的时候  –  LBS1.0  
MongoDB
Slave
  
MongoDB  
Slave  
  
  
MongoDB  
Slave  
MongoDB
Master
开发部署简单  
•  原生支持空间索引  
•  实时更新支持较好  
•  开发成本低  
扩展性较好  
•  读写分离  
•  负载均衡  
11
MongoDB  as  LBS  
API接入层  
位置上报服务  
Master  MongoDB   Slave  MongoDB  
派
单  
抢
单  
人
工
派
单  
推
单  
12
MongoDB空间搜索原理  
MongoDB内存过滤、合并  
搜 索 结 果
空间搜索操作
为CPU密集型  
13
MongoDB瓶颈  
读写密集时出现问题  
从服务器CPU负载急剧上升  
查询性能急剧降低  
查询吞吐量大幅降低  
主从复制出现较大的延迟  
原因分析  
每一次LBS查询,会分解成许多次
单独的子查询  
增大查询锁等待概率  
结果合并、内存过滤消耗CPU  
14
优化方案:MongoDB  
库级锁问题  
升级MongoDB(2.8+)  
支持行级锁  
CPU密集问题  
分片  
单库写控制在4000以内  
支持目前的骑手数,至少需要
4个MongoDB实例  
资源浪费严重  
15
优化方案:自研LBS  
GeoHash 存储系统   自研LBS
能否基于GeoHash算法,自己实现一个LBS空间搜索服务?
16
GeoHash  
定义:将一个经纬度信息,转换成一个可排序、可比较的字符串编码;  
技术能力:负责子系统、技术规划、方法论沉淀、部⻔门级影响力;	
  
17
GeoHash  
(116.389550,39.928167)
wx4g0s8q3jf9
降维   前缀包含   邻域相似  
18
GeoHash  
GeoHash原理  
19
GeoHash  
10111 00011 11010 01011
11100 11101 00100 01111
以北海公园为例  
•  经纬度:116.389550,39.928167  
纬度:39.928167     经度:116.389550  
20
LBS2.0  -  存储系统选择  
关系数据库   No-SQL  
GeoHash 存储系统   自研LBS
MySQL   Redis  
21
LBS2.0:  MySQL  +  GeoHash  
API接入层  
LBS  
GeoHash  
Master  MySQL   Slave  MySQL  
派
单  
抢
单  
人
工
派
单  
推
单  
22
LBS2.0:  MySQL  +  GeoHash  
数据库查找   内存过滤  
§   选择合适的GeoHash位数  
§   选择当前坐标的格子以及
周边若干个格子  
§   拼成SQL语句  
§ 内存过滤SQL
语句返回的结
果,刨除不符
合半径限制的
记录  
如何进行空间搜索?  
23
LBS2.0:  MySQL  +  GeoHash  
标准的GeoHash缺陷  
Base32编码,每一个字符代表5个bit位  
wx4g0s8代表面积比Wx4g0s8q大32倍  
GeoHash块可选范围小  
纯二进制的GeoHash字符串缺陷  
字符串太长、浪费空间  
前缀匹配效率低  
60Bit  GeoHash优点  
可选访问宽  
节省存储空间  
搜索效率高  
如何选择合适GeoHash格式?  
24
LBS2.0:  MySQL  +  GeoHash  
Base32格式  
GeoHash  
左对齐转成64
位整形  
右移4位  
列名   字段类型   描述  
bm_user_id   bigint   骑手ID  
bm_user_lat   int   骑手纬度  
bm_user_lng   int   骑手经度  
geohash_long   bigint   60bit  GEOHASH值  
bm_org_id   int   组织ID  
utime   int   上传时间  
25
LBS2.0:  MySQL  +  GeoHash  
方案优点  
研发经验丰富  
运维比较成熟  
方案缺点  
性能存在瓶颈  
扩展困难  
单数据库写压力须控制在2000以内  
仅支持万级别的骑手数  
分库分表方案  
26
LBS2.0:  MySQL  +  GeoHash  
方 案 复 杂
资 源 浪 费
未 予 实 施
分库分表  
•  Sharding  Key:  21Bit  GeoHash值  
•  可表示19.5KM  *  19.5KM的矩形  
•  绝大部分查询可在一个表中完成  
•  极端情况需要查询四个库(表)  
资源评估  
•    写多、读少、数据量少  
•  支撑1.5W上报QPS需分片8个主库  
分库分表方案?  
27
LBS2.0:  Redis  +  GeoHash  
API接入层  
LBS  
GeoHash  
Redis  Cluster  
派
单  
抢
单  
人
工
派
单  
推
单  
28
LBS2.0:  Redis  +  GeoHash  
数据冗余  
如何解决多维度查询问题?  
l  如何查询当前骑手的实时位置?  
l  派单:如何查询一个组织内的所有骑手的实时位置?  
l  众包抢单:如何查询一定范围内的所有骑手?  
•    3公里半径内  
•    多边形范围内  
29
LBS2.0:  Redis  +  GeoHash  
KEY   VALUE  
PREFIX:{riderId}   (lat,  lng,  timestamp,  …)  
KEY   FIELD   VALUE  
PREFIX:
{orgId}  
RiderId   (lat,  lng,  
timestamp,  …)  
KEY-MAP
结构  
KEY   FIELD   VALUE  
PREFIX:
{geohash}  
RiderId   (lat,  lng,  
timestamp,  …)  
l  如何查询当前骑手的实
时位置?  
l  骑手到骑手坐标的映射  
l  如何查询一定范围内所
有骑手?  
l  GeoHash到骑手坐标映射  
l  如何查询组织内骑手实时
位置?  
l  组织到骑手坐标映射  
30
LBS2.0:  Redis  +  GeoHash  
GeoHash位数选择  
27bit,代表的格子大小约为4.5Km2  
每次取9个GeoHash格子,可以过滤出周围3Km范围内所有骑手  
Key-Map大小控制  
hGetAll获取Map所有内容  
Map大小不宜超过1000  
31
LBS2.0:  Redis  +  GeoHash  
骑手在格子间漂移的问题?  
  骑手坐标上传时  
l  取出上一次骑手实时坐标  
l  比较GeoHash格子是否有变化  
•  有变化:删除上一次格子中的信  
l  存储当前骑手经纬对应的GeoHash格子  
每次上传大约操作Redis  3~4次  
32
LBS2.0:  Redis  +  GeoHash  
效果评估  
资源消耗  
l  10GB  Redis主内存  
QPS最高支持  
l  写:10W,是当前峰值流量的6倍  
l  读:20W,是当前峰值流量的16倍  
接口性能  
l  位置上报接口:TP99  约4ms  
l  查询接口:TP99  小于10ms  
技术分享
  
谢谢!  
  

美团点评沙龙12-LBS空间搜索架构的优化历程

  • 1.
  • 2.
    2 关于.  我   2009.012011.01 2015.11   现在   MIM  Software   爱奇艺   新美大       云平台     1:云推送   2:Passport   3:爱奇艺泡泡   技术研发部     1:医疗软件开发   配送事业部     1:LBS   2:动态定价   3:动态预测  
  • 3.
    3 目录   l  关于 .  我   l  LBS与O2O   l  主要业务场景   l  LBS服务演化   l  MongoDB方案   l  MySQL+GeoHash方案   l  Redis+GeoHash方案  
  • 4.
    4 LBS与O2O   LBS   团购  打车   外卖配送   餐饮   商超   电影票  
  • 5.
    5 配   送   系  统   如何将用户的订单交给合适的骑手配送?   主要业务场景   商家在哪里?   入驻时确定   坐标固定   用户在哪里?   下单时确定   坐标固定   骑手在哪里?   实时变化   坐标不固定  
  • 6.
  • 7.
    7 主要业务场景 订单找   骑手  派单  推单   抢单   人工派单   订单找   骑手  
  • 8.
  • 9.
    9 业内解决方案   更新索引效率低   不适合实时更新场景  GeoHash   笛卡尔   层级   R树   MongoDB    Postgre/PostGIS   Lucene/Solr/ES  
  • 10.
    10 当配送小的时候  –  LBS1.0  MongoDB Slave   MongoDB   Slave       MongoDB   Slave   MongoDB Master 开发部署简单   •  原生支持空间索引   •  实时更新支持较好   •  开发成本低   扩展性较好   •  读写分离   •  负载均衡  
  • 11.
    11 MongoDB  as  LBS  API接入层   位置上报服务   Master  MongoDB   Slave  MongoDB   派 单   抢 单   人 工 派 单   推 单  
  • 12.
    12 MongoDB空间搜索原理   MongoDB内存过滤、合并   搜索 结 果 空间搜索操作 为CPU密集型  
  • 13.
    13 MongoDB瓶颈   读写密集时出现问题   从服务器CPU负载急剧上升  查询性能急剧降低   查询吞吐量大幅降低   主从复制出现较大的延迟   原因分析   每一次LBS查询,会分解成许多次 单独的子查询   增大查询锁等待概率   结果合并、内存过滤消耗CPU  
  • 14.
    14 优化方案:MongoDB   库级锁问题   升级MongoDB(2.8+)  支持行级锁   CPU密集问题   分片   单库写控制在4000以内   支持目前的骑手数,至少需要 4个MongoDB实例   资源浪费严重  
  • 15.
    15 优化方案:自研LBS   GeoHash 存储系统  自研LBS 能否基于GeoHash算法,自己实现一个LBS空间搜索服务?
  • 16.
  • 17.
  • 18.
  • 19.
    19 GeoHash   10111 0001111010 01011 11100 11101 00100 01111 以北海公园为例   •  经纬度:116.389550,39.928167   纬度:39.928167     经度:116.389550  
  • 20.
    20 LBS2.0  -  存储系统选择  关系数据库   No-SQL   GeoHash 存储系统   自研LBS MySQL   Redis  
  • 21.
    21 LBS2.0:  MySQL  + GeoHash   API接入层   LBS   GeoHash   Master  MySQL   Slave  MySQL   派 单   抢 单   人 工 派 单   推 单  
  • 22.
    22 LBS2.0:  MySQL  + GeoHash   数据库查找   内存过滤   §   选择合适的GeoHash位数   §   选择当前坐标的格子以及 周边若干个格子   §   拼成SQL语句   § 内存过滤SQL 语句返回的结 果,刨除不符 合半径限制的 记录   如何进行空间搜索?  
  • 23.
    23 LBS2.0:  MySQL  + GeoHash   标准的GeoHash缺陷   Base32编码,每一个字符代表5个bit位   wx4g0s8代表面积比Wx4g0s8q大32倍   GeoHash块可选范围小   纯二进制的GeoHash字符串缺陷   字符串太长、浪费空间   前缀匹配效率低   60Bit  GeoHash优点   可选访问宽   节省存储空间   搜索效率高   如何选择合适GeoHash格式?  
  • 24.
    24 LBS2.0:  MySQL  + GeoHash   Base32格式   GeoHash   左对齐转成64 位整形   右移4位   列名   字段类型   描述   bm_user_id   bigint   骑手ID   bm_user_lat   int   骑手纬度   bm_user_lng   int   骑手经度   geohash_long   bigint   60bit  GEOHASH值   bm_org_id   int   组织ID   utime   int   上传时间  
  • 25.
    25 LBS2.0:  MySQL  + GeoHash   方案优点   研发经验丰富   运维比较成熟   方案缺点   性能存在瓶颈   扩展困难   单数据库写压力须控制在2000以内   仅支持万级别的骑手数   分库分表方案  
  • 26.
    26 LBS2.0:  MySQL  + GeoHash   方 案 复 杂 资 源 浪 费 未 予 实 施 分库分表   •  Sharding  Key:  21Bit  GeoHash值   •  可表示19.5KM  *  19.5KM的矩形   •  绝大部分查询可在一个表中完成   •  极端情况需要查询四个库(表)   资源评估   •   写多、读少、数据量少   •  支撑1.5W上报QPS需分片8个主库   分库分表方案?  
  • 27.
    27 LBS2.0:  Redis  + GeoHash   API接入层   LBS   GeoHash   Redis  Cluster   派 单   抢 单   人 工 派 单   推 单  
  • 28.
    28 LBS2.0:  Redis  + GeoHash   数据冗余   如何解决多维度查询问题?   l  如何查询当前骑手的实时位置?   l  派单:如何查询一个组织内的所有骑手的实时位置?   l  众包抢单:如何查询一定范围内的所有骑手?   •   3公里半径内   •   多边形范围内  
  • 29.
    29 LBS2.0:  Redis  + GeoHash   KEY   VALUE   PREFIX:{riderId}   (lat,  lng,  timestamp,  …)   KEY   FIELD   VALUE   PREFIX: {orgId}   RiderId   (lat,  lng,   timestamp,  …)   KEY-MAP 结构   KEY   FIELD   VALUE   PREFIX: {geohash}   RiderId   (lat,  lng,   timestamp,  …)   l  如何查询当前骑手的实 时位置?   l  骑手到骑手坐标的映射   l  如何查询一定范围内所 有骑手?   l  GeoHash到骑手坐标映射   l  如何查询组织内骑手实时 位置?   l  组织到骑手坐标映射  
  • 30.
    30 LBS2.0:  Redis  + GeoHash   GeoHash位数选择   27bit,代表的格子大小约为4.5Km2   每次取9个GeoHash格子,可以过滤出周围3Km范围内所有骑手   Key-Map大小控制   hGetAll获取Map所有内容   Map大小不宜超过1000  
  • 31.
    31 LBS2.0:  Redis  + GeoHash   骑手在格子间漂移的问题?    骑手坐标上传时   l  取出上一次骑手实时坐标   l  比较GeoHash格子是否有变化   •  有变化:删除上一次格子中的信   l  存储当前骑手经纬对应的GeoHash格子   每次上传大约操作Redis  3~4次  
  • 32.
    32 LBS2.0:  Redis  + GeoHash   效果评估   资源消耗   l  10GB  Redis主内存   QPS最高支持   l  写:10W,是当前峰值流量的6倍   l  读:20W,是当前峰值流量的16倍   接口性能   l  位置上报接口:TP99  约4ms   l  查询接口:TP99  小于10ms  
  • 33.