為什麼geometry+GIST比geohash+BTREE更適合空間搜尋-多出的不僅僅是20倍效能提升
標籤
PostgreSQL , gist , btree , 空間索引 , 範圍掃描
背景
在PostgreSQL中,支援geohash, geometry, geograph三種空間儲存結構。
1、geohash,很多庫都支援它,因為簡單,將地球作為標準化的球體,展開抽象為一個平面,劃分為若干個小方格,進行編碼,相鄰的小方格的編碼字首一樣。
geohash 每一個小方塊的精度與編碼長度有關(這個說法也不完全準確,因為是基於地球是標準球體的前提),如下:
2、由於地球並非標準球體,也非標準的橢球體,所以geohash精度有硬性的缺陷,geometry與geograph型別,可以解決這個問題。
對於GIS來說,首先是座標系,有兩種:一種是球座標(地理座標),另一種是平面座標(投影座標)。
球座標通常用於計算,平面座標通常用於展示(也可以計算)。
投影座標是從球座標投影后展開得來(用一個圓柱將地球包起來,把地球當成會發光的光源,投影后,將圓柱展開得到),投影的範圍越大,精度就越低。範圍越小,
計算距離,應該考慮到被計算的兩點所在處的地球特性(spheroid)。這樣計算得到的距離才是最精確的。
geometry和geography型別的選擇,建議使用geometry,既能支援球座標系,又能支援平面座標系。主要考慮到使用者是否瞭解位置所在處的地理特性,選擇合適的座標系。
目前用得最多的有SRID=4326球座標,SRID為EPSG:3857的墨卡託投影座標。
再來說geometry和geography兩種型別,geometry支援平面物件也支援空間物件,而geography則僅支援空間物件。
geometry支援更多的函式,一些幾何計算的代價更低。
geography支援的函式略少,計算代價更高。但是對於跨度較大地域性的業務,就需要使用geography,因為它的精度不受制於區域。
If your data is contained in a small area, you might find that choosing an appropriate
projection and using GEOMETRY is the best solution, in terms of performance and functionality available.
If your data is global or covers a continental region, you may find that GEOGRAPHY
allows you to build a system without having to worry about projection details.
You store your data in longitude/latitude, and use the functions that have been defined on GEOGRAPHY.
If you don`t understand projections, and you don`t want to learn about them,
and you`re prepared to accept the limitations in functionality available in GEOGRAPHY,
then it might be easier for you to use GEOGRAPHY than GEOMETRY.
Simply load your data up as longitude/latitude and go from there.
除了空間模型上的差異,geohash與geometry, geograph還有功能、效能上的差異。
效能方面主要體現在GEOHASH的編碼精度會帶來一些問題:
1、由於GEOHASH編碼的問題,我們在搜尋某一個點附近N米內的物件時,會引入空間放大,理論上我們要的是以目標點為中心,距離為半徑的一個圓內的資料。
如果只看字首的話,這個放大會隨著編碼長度縮短而級數增加。
然而,使用geometry的距離搜尋,不會引入放大問題,使用GIST索引按距離排序輸出加上st_dwithin約束,返回的一定是在圓圈內的資料,並且不造成額外的RECHECK FILTER。
又比如在GIS北京峰會上探探的一個案例,搜尋附近的10家餐館,在POI密集的地方,一個小的BOX可就圈出幾千家餐館了,而在偏遠地區,你就需要一個較大的BOX,還不一定能圈到10家餐館。
2、當我們需要搜尋的是任意多邊形時,GEOHASH也無法滿足需求,需要進行大範圍的匹配,然後再逐條進行空間計算過濾。
幾種地理資料的掃描方法
1、geohash 字首掃描,匹配在這個正方形塊內的資料
postgres=# create table t_test(
id int,
pos text, -- geohash
geo geometry -- geometry
);
CREATE TABLE
postgres=# insert into t_test
select id,
st_geohash(st_setsrid(st_point(x,y),4326), 13),
st_setsrid(st_point(x,y),4326)
from (
select id, 120+30*random() x, 68+5*random() y
from generate_series(1,100000) t(id)
) t;
INSERT 0 100000
postgres=# select * from t_test limit 10;
id | pos | geo
----+---------------+----------------------------------------------------
1 | yu0j8y2pxsezp | 0101000020E61000000000625C21F25E400000510228205140
2 | zhsfe7t2cbtzz | 0101000020E6100000008049BE8DBA61400080CB2C5DB15140
3 | zhcydqptr7bkd | 0101000020E6100000000061ED403261400000A01B4B395240
4 | yuhdce4q6u7t6 | 0101000020E610000000808C51B6446040008055F70F005140
5 | yus98nqjtdf4r | 0101000020E610000000803D75C54260400080923722A75140
6 | zk9grxnsqxv98 | 0101000020E61000000000787897A16240008086A312BB5140
7 | yurhhfh33u5xm | 0101000020E61000000080C877DEB96040008031E3B7675140
8 | zhk5qv4vhe10k | 0101000020E610000000002A889E9D61400080CA5360605140
9 | zhm49th6m0h5y | 0101000020E61000000000C79D4DC361400000B456E8575140
10 | zh95n0wvxkpv5 | 0101000020E610000000808F92BE1561400000A9D5FCB55140
(10 rows)
postgres=# create index idx_t_test_1 on t_test (pos text_pattern_ops);
CREATE INDEX
postgres=# explain select * from t_test where pos ~ `^yuhdce4`;
QUERY PLAN
-----------------------------------------------------------------------------
Index Scan using idx_t_test_1 on t_test (cost=0.42..2.64 rows=10 width=50)
Index Cond: ((pos ~>=~ `yuhdce4`::text) AND (pos ~<~ `yuhdce5`::text))
Filter: (pos ~ `^yuhdce4`::text)
(3 rows)
postgres=# explain select * from t_test where pos like `yuhdce4%`;
QUERY PLAN
-----------------------------------------------------------------------------
Index Scan using idx_t_test_1 on t_test (cost=0.42..2.64 rows=10 width=50)
Index Cond: ((pos ~>=~ `yuhdce4`::text) AND (pos ~<~ `yuhdce5`::text))
Filter: (pos ~~ `yuhdce4%`::text)
(3 rows)
2、geohash 範圍掃描,匹配在一個連續Z空間中的一段小方格
將二進位制編碼的結果填寫到空間中,當將空間劃分為四塊時候,編碼的順序分別是左下角00,左上角01,右下腳10,右上角11,也就是類似於Z的曲線,當我們遞迴的將各個塊分解成更小的子塊時,編碼的順序是自相似的(分形),每一個子快也形成Z曲線,這種型別的曲線被稱為Peano空間填充曲線。
postgres=# explain select * from t_test where pos ~>=~ `yuhdce4` and pos ~<=~ `yuhdcej`;
QUERY PLAN
----------------------------------------------------------------------------
Index Scan using idx_t_test_1 on t_test (cost=0.42..2.64 rows=1 width=50)
Index Cond: ((pos ~>=~ `yuhdce4`::text) AND (pos ~<=~ `yuhdcej`::text))
(2 rows)
3、經度與維度範圍掃描,將經度與維度分開兩個欄位儲存。掃描得到的是一個落在經緯度區間內的長方形區間。
create table t_geo (id int, x float, y float);
insert into t_geo
select id, 120+30*random() x, 68+5*random() y
from generate_series(1,100000) t(id) ;
postgres=# create index idx_t_geo_1 on t_geo (x,y);
CREATE INDEX
postgres=# explain select * from t_geo where x >= 120 and x <=124 and y >= 68 and y <=71;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------------
Index Scan using idx_t_geo_1 on t_geo (cost=0.42..1029.31 rows=7810 width=20)
Index Cond: ((x >= `120`::double precision) AND (x <= `124`::double precision) AND (y >= `68`::double precision) AND (y <= `71`::double precision))
(2 rows)
4、GEOMETRY GIS空間掃描
gist 是基於gist的r-tree,每一層為一個BOUND,當需要搜尋包含、相交、近鄰時,可以快速定位到與你輸入的物件 包含、相交、近鄰 的物件。
詳細結構建本文末尾的參考部分。
create index idx_t_test_2 on t_test using gist (geo);
GIST索引不僅支援空間檢索,還支援空間排序。
postgres=# explain select * from t_test where st_dwithin(geo, st_setsrid(st_point(121, 70), 4326), 10000) order by geo <-> st_setsrid(st_point(121, 70), 4326);
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------------------
Index Scan using idx_t_test_2 on t_test (cost=0.28..29263.75 rows=6667 width=58)
Index Cond: (geo && `0103000020E6100000010000000500000000000000804BC3C0000000000065C3C000000000804BC3C00000000000ABC3400000000080C4C3400000000000ABC3400000000080C4C340000000000065C3C000000000804BC3C0000000000065C3C0`::geometry)
Order By: (geo <-> `0101000020E61000000000000000405E400000000000805140`::geometry)
Filter: ((`0101000020E61000000000000000405E400000000000805140`::geometry && st_expand(geo, `10000`::double precision)) AND _st_dwithin(geo, `0101000020E61000000000000000405E400000000000805140`::geometry, `10000`::double precision))
(4 rows)
在LBS專案中,按距離由近到遠排序輸出,是非常強烈的需求。
效能對比
GIST索引比兩個欄位複合索引要快很多,原因是複合索引在驅動列使用範圍時,這個範圍下的所有ENTRY都要被掃描。
同樣的測試對比如下:
《PostgreSQL 黑科技 range 型別及 gist index 20x+ speedup than Mysql index combine query》
小結
1、GEOHASH,適合對精度沒有要求(例如本土化,小範圍的業務),並且捨得浪費計算資源的場景(因為顆粒度大,所以通過索引圈出的區域,可能有很多無效資料,需要大量RECHECK),同時GEOHASH不支援排序,所以需要額外的排序開銷。
2、geometry,空間索引,適合對精度要求高的場景,且節約資源。適合專業的GIS業務。geometry使用時,需要注意選擇正確的座標系。geograph則對座標系沒有要求。
3、在一個物件稀疏的區域,圈出附近100個點。與在一個物件密集的區域,圈出附近100個點。使用GEOHASH完全不知所措,因為你不知道該用多大的PREFIX合適,而使用geometry+gist,非常容易且高效率的解決這個問題。
select * from tbl where pos ~ `^geohash_多長合適呢? 不知道` limit 100;
select * from tbl order by geo <-> 點 limit 100;
在PG裡面,我們同時支援geohash, geometry, geograph三種空間儲存,你喜歡什麼樣的姿勢,就用什麼樣樣的姿勢。這就是我們喜愛的PostgreSQL。
參考
《PostgreSQL 黑科技 – 空間聚集儲存, 內窺GIN, GiST, SP-GiST索引》
《PostGIS空間索引(GiST、BRIN、R-Tree)選擇、優化 – 阿里雲RDS PostgreSQL最佳實踐》
《自動選擇正確索引訪問介面(btree,hash,gin,gist,sp-gist,brin,bitmap…)的方法》
《HTAP資料庫 PostgreSQL 場景與效能測試之 6 – (OLTP) 空間應用 – KNN查詢(搜尋附近物件,由近到遠排序輸出)》
《GIS附近查詢效能優化 – PostGIS long lat geometry distance search tuning using gist knn function》
《PostGIS 距離計算建議 – 投影 與 球 座標系, geometry 與 geography 型別》
http://www.cnblogs.com/LBSer/p/3310455.html
相關文章
- 為什麼 async/await 不僅僅是句法糖AI
- 為什麼說六西格瑪不僅僅是資料運算
- 重要 | Spark和MapReduce的對比,不僅僅是計算模型?Spark模型
- 為什麼ElasticSearch比MySQL更適合全文索引ElasticsearchMySql索引
- 為什麼說rollup比webpack更適合打包庫Web
- 攻防世界-不僅僅是RSA
- NoSQL——not onlySQL不僅僅是SQLSQL
- Redis不僅僅是快取,還是……Redis快取
- bing/google/百度高階搜尋技巧--搜尋時關鍵詞不拆分,僅搜尋某個站點或僅要求pdf/doc格式搜尋結果等等Go
- 電子競技,不僅僅是遊戲遊戲
- AI是一個真正的系統而不僅僅是軟體AI
- 電視盒子不僅僅是點播,功能細分的電視盒子實用性更強
- CDP營銷方案 不僅僅是資料整合!
- 雲不僅僅是一種全新的IT基礎設施
- 不僅僅是因為隨機性強,Roguelike遊戲究竟有啥好玩?隨機遊戲
- SmartCode—不僅僅是功能強大的程式碼生成器
- 資料隱私不僅僅是指機密性
- OneClock - 不僅僅是桌面極簡翻頁時鐘
- Apache Flink,流計算?不僅僅是流計算!Apache
- Kibana:Kibana 不僅僅是針對開發者的 - 它是為所有人開發的
- 一類適合記憶化搜尋的區間dp
- 為什麼說Docker 不適合跑 MySQL?DockerMySql
- python為什麼不適合web開發PythonWeb
- 你和阿里資深架構師之間,差的不僅僅是年齡(進階必看)阿里架構
- 為尋求Windows系統效能的提升,微軟已宣佈放棄僅推出四年的ElectronWindows微軟
- DBA不僅僅是管理資料庫--也要管理好需求資料庫
- 哪些人更適合學習Python?為什麼?Python
- 賽博朋克中的設計核心(一):不僅僅是日本文化
- 【虹科分享】Redis 不僅僅是記憶體資料庫Redis記憶體資料庫
- DBA不僅僅是管理資料庫--也要管理中介軟體資料庫
- 從“烏雞”到5G,不僅僅是諧音梗
- 深入理解BERT Transformer ,不僅僅是注意力機制ORM
- 不僅僅是前端er——折騰伺服器武裝自己前端伺服器
- IBM Watson啟示錄:AI不應該僅僅是炫技IBMAI
- 學習風變程式設計,學會的不僅僅是程式設計程式設計
- 深入認識 vue-cli:能做的不僅僅是初始化 vue 工程Vue
- AI之父:大模型不僅僅是預測下一個符號AI大模型符號
- SOLIDWORKS軟體不僅僅是三維機械設計軟體Solid