http://www.wubiao.info/470
前兩篇文章:
查詢附近的xxx 球面距離以及Geohash方案探討 (http://www.wubiao.info/372)
微信、陌陌 架構方案分析 (http://www.wubiao.info/401)
探討了,LBS查詢附近的XXX;其中包括了,Mysql自定義儲存函式方案,以及通過GeoHash、redis自建索引方案。
===============================================================
今天分享兩種,利用GeoHash封裝成內建資料庫函式的簡易方案;
A:Mysql 內建函式方案,適合於已有業務,新增加LBS功能,增加經緯度欄位方可,避免資料遷移
B:Mongodb 內建函式方案,適合中小型應用,快速實現LBS功能,效能優於A(推薦)
===============================================================
方案A: (MySQL Spatial)
1、先簡歷一張表:(MySQL 5.0 以上 僅支援 MyISAM 引擎)
1
2
3
4
5
6
7
8
9
|
CREATE TABLE address (
address CHAR (80) NOT NULL ,
address_loc POINT NOT NULL ,
PRIMARY KEY (address)
); |
空間索引:
1
|
ALTER TABLE address ADD SPATIAL INDEX (address_loc);
|
插入資料:(注:此處Point(緯度,經度) 標準寫法)
1
2
3
|
INSERT INTO address VALUES ( `Foobar street 12` , GeomFromText( `POINT(30.620076 104.067221)` ));
INSERT INTO address VALUES ( `Foobar street 13` , GeomFromText( `POINT(31.720076 105.167221)` ));
|
查詢: 查詢(30.620076,104.067221)附近 10 公里
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
SELECT *
FROM address
WHERE MBRContains
(
LineString
(
Point
(
30.620076 + 10 / ( 111.1 / COS(RADIANS(104.067221))),
104.067221 + 10 / 111.1
),
Point
(
30.620076 - 10 / ( 111.1 / COS(RADIANS(104.067221))),
104.067221 - 10 / 111.1
)
),
address_loc
)
|
方案B:
1、先建立一張簡單的表user,兩條資料如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
{ "_id" : ObjectId( "518b1f1a83ba88ca60000001" ),
"account" : "simplephp1@163.com" ,
"gps" : [
104.067221,
30.620076
]
} { "_id" : ObjectId( "518b1dae83ba88d660000000" ),
"account" : "simplephp6@163.com" ,
"gps" : [
104.07958,
30.653936
]
} |
其中,gps為二維陣列,分別為經度,緯度
(注:此處必須按照(經度,緯度)順序儲存。我們平時表示經緯度,都是(緯度,精度),此處這種方式有木有很親民)
2、使用之前,先建立二維索引
//建立索引 最大範圍在經度-180~180
1
|
db. user .ensureIndex({ "gps" : "2d" },{ "min" :-180, "max" :180})
|
//刪除索引
1
|
db. user .dropIndex({ "gps" : "2d" })
|
3、Mongodb有兩中方式可以查詢附近的XXX;其中方案2)會返回距離(推薦)
1)標準查詢,為地球經緯度查詢內建;引數一為查詢條件利用$near查詢附近,引數二$maxDistance為經緯弧度(1° latitude = 111.12 kilometers)即 1/111.12,表示查詢附近一公里。
1
|
db. user .find({ gps :{ $near : [104.065847, 30.657554] , $maxDistance : 1/111.12} })
|
2)執行命名方式,模擬成一個圓球;引數一指定geoNear方式和表名;引數二座標,引數三是否為球形,引數四弧度(弧度=弧長/半徑 一千米的弧度1000/6378000),引數五指定球形半徑(地球半徑)
1
|
db.runCommand({geoNear: `user` , near:[104.065847, 30.657554], spherical: true , maxDistance:1000/6378000, distanceMultiplier:6378000});
|