HBase与时空索引技术
title: HBase與時空索引技術
date: 2021-05-13 10:04:05
tags:
- HBase
- GIS
所謂時空數據,顧名思義,包含了兩個維度的信息:空間信息與時間信息。空間信息,以地理位置點最為基礎,還包括線、多邊形以及更為復雜的多維結構。最典型的時空數據,莫過于移動對象的軌跡點數據,如每隔5秒鐘記錄的車輛實時位置信息。這類數據,在物聯網領域司空見慣,在可預見的未來,這類數據將會出現爆炸性的增長。
用HBase存放時空數據
時空數據,尤其是移動對象位置點數據,結構簡單,但關于吞吐量的要求卻往往很高。單從這點信息來看,這類數據屬于HBase的適用范疇。
先不談論時間緯度與復雜的多邊形數據,我們先從最簡單的地理位置點數據開始,探討一下如果用HBase來存儲這類數據會遭遇哪些技術挑戰。
地理位置點Point中包含兩個信息:經度X與緯度Y,按照傳統的思路,將一個Point存成一行數據,而經度X與緯度Y則分別是構成這行數據的兩個列,可以為每一個Point設置一個ID,并以此ID作為數據RowKey。所有的Points在表中按ID字典順序存放。
圍繞地理位置點的典型的查詢方式,如“查詢某一個建筑附近有哪些酒店?”,按照剛剛表設計思路,數據如按主鍵ID順序存放,某個建筑附近的所有酒店,它們在主鍵順序上卻未必是相鄰的,因此,這個查詢可能涉及掃描大量的無關數據。
可見,針對最簡單的時空數據類型,傳統HBase表設計思路已經顯得非常低效,傳統關系型數據庫基于B-Tree的數據組織結構也更是疲于應對。再放大到整個時空數據的范疇,要支持的典型場景更為復雜:
- 查詢某一個地理區域在某個歷史時間范圍內發生的關鍵事件
- 查詢某一個地理區域的人流量、車流量信息
- 查詢某一天上午先后在A,B,C三個地理區域出現過的具有某些特征的人
- 查詢某嫌疑人在某一天的行動軌跡
歸根結底,HBase中的數據按照RowKey單維度排序組織,而我們現在卻面臨的是一個多維數據問題。因此,HBase如果想很好的支持時空數據的存儲,需要引入時空索引技術。
從上面的查詢場景來看,原生HBase的訪問接口難以直接支持這些能力,因此,時空數據領域需要更加專業的訪問接口,這是GeoMesa項目存在的關鍵原因。
常見的時空索引技術
關于時空索引,已經有不少常見的技術,這里,我們主要介紹一下R-Trees、Quad-Trees、K-D Trees以及Space Filling Curve這四種技術。
R-Trees
R-Trees源自論文《R-Trees: A Dynamic Index Structure For Spatial Searching》,下面的圖也源自此論文:
R-Trees基于這樣的思想設計:
每一個空間對象,如一個多邊形區域,都存在一個最小的矩形,能夠恰好包含此時空對象,如上圖中的矩形R6所包含的彎月形區域。這個最小包圍矩形被稱之為MBR(Minimum Bounding Rectangle)。
多個相鄰的矩形,可以被包含在一個更大的最小包圍矩形中。如R6、R9以及R10三個矩形,可以被包含在R3中,而R11與R12則被包含在R4中。
繼續迭代,總能找到若干個最大的區域,以一種樹狀的形式,將所有的時空對象給容納進去,如上圖中的R1, R2。這樣,整個樹狀結構,呈現如下:
從最小的矩形區域,到最大的矩形區域,就好比地圖中的行政區域劃分:村 -> 縣 -> 市 -> 省份。查詢時,先從鎖定的最大區域開始,逐級縮小比例尺后,就可找到最終的對象。如若將上圖中的R1與R2理解成兩個平級的”行政區域”,卻又存在本質的區別:不同的行政區域,并不存在相互重疊,而R1與R2卻可能是重疊的。
R-Trees的定義:
\1. 每一個Leaf Node包含m到M個索引記錄(Root節點除外)。
\2. 每一個索引記錄是一個關于(I, tuple-identifier)的二元組信息,I是空間對象的最小包圍矩形,而tuple-identifier用來描述空間對象本身。
\3. 每一個Non-leaf節點,包含m到M個子節點(Root節點除外)。
\4. Non-leaf節點上的每一個數據元素,是一個(I, child-pointer)的二元組信息,child-pointer指向其中一個子節點,而I則是這個子節點上所有矩形的最小包圍矩形。
如上圖中,R3、R4以及R5,共同構成一個non-leaf節點,R3指向包含元素R8,R9以及R10的子節點,這個指針就是child-pointer,而與R8,R9以及R10相關的最小包圍矩形,就是I。
\5. Root節點至少包含兩個子節點,除非它本身也是一個Leaf Node。
\6. 所有的Leaf Nodes都出現在樹的同一層上。
從定義上來看,R-Trees與B-Trees存在諸多類似:Root節點與Non-Leaf節點,均包含限定數量的子節點;所有的Leaf Nodes都出現在樹的同一層上。這兩種樹也都是自平衡的。
前面也已經提到了,B-Trees主要用來存放一維排序的數據元素,而R-Tree存放的則是多維空間數據元素。從查詢方式上來看,兩者也存在顯著的差異:B-Trees更擅長于數據點查,它的設計并不利于數據的范圍查詢。基于空間元素的查詢,卻以范圍查詢為主,而且往往需要對多個子樹進行并行查詢,例如,在地圖上劃定某一個區域,查詢這個區域內有哪些公園,可能有多個子樹都與劃定的這個區域存在交集。從這一點看來,R-Tree的搜索性能其實并沒有很好的保障。
R-Trees有多種變種,如R±Trees,R*-Trees,X-Trees, M-Trees,BR-Trees等等,不再展開過多的介紹。
Quad-Trees
同很多數據結構類似,Quad-Trees也存在多種變種,最初的Quad-Trees設計源自論文《Quad Trees: A Data Structure for Retrieval on Composite Keys》,下圖源自論文,是關于Quad-Trees的直觀呈現:
上圖中的A,B,C,E,F,G均為Point,以每一個Point作為中心點,可以將一個區間分成4個象限。
假設,先寫入Point A,以A為中心,將整個區間分成了4個象限。
寫入Point B,Point B位于A的東北象限中,同樣,以B為中心,依然可以將A東北象限進一步細分為4個子象限。
寫入Point C,Point C位于A的東南象限中,以C為中心,可以將A的東南象限細分成4個子象限。
……
任何新寫入的一個Point,總能找到一個某一個已存在的Point的子象限,來存放這個新的Point。
整個樹狀結構呈現如下:
可見,Quad-Trees有幾個鮮明的特點:1. 對于非葉子節點,均包含4個子節點,對應4個面積不一的象限。2.**不平衡,樹的結構與數據的寫入順序直接相關。**3.有空的Leave Nodes,且所有的Leave Nodes則是”參差不齊”的(并不一定都出現在樹的同一層中)。4.數據既可能出現在分枝節點中,也可能出現在葉子節點中。
因為Quad-Trees存在諸多變種,為了有所區分,上面提到的最簡單的這種Quad-Tree,被稱之為Point Quad-Trees。還有一種典型的Quad-Trees,被稱之為Point Region QuadTrees(簡稱為PR QuadTrees):
PR Quad-Trees中,每一次迭代出來的4個象限,面積相同,且不依賴于用戶數據Point作為分割點,或者說,數據分區與用戶數據無關。每一個劃分出來的子象限中,只允許存在一個Point。
與Point Quad-Trees相比,PR Quad-Trees允許兩份不同的數據集,擁有相同的分區信息。但PR Quad-Trees存在的問題也明顯:1. 兩個相鄰的Points,可能在樹的Level高度上相隔甚遠。2.兩份數據集如果追求相同的分區信息,可能需要進行足夠粒度的分割,這可能導致空間浪費。
K-D Trees
K-D Trees是一種針對高維點向量數據的索引結構,一棵簡單的K-D Tree如下圖所示(原圖源自James Fogarty的”K-D Trees and Quad Trees”,但為了更直觀,關于分區分割線的線條做了改動):
與Quad Trees思想類似,K-D Trees也是將整個區間進行不斷分割,不同之處在于,Quad Trees每一次迭代,將一個區間分割成四個象限,而K-D Trees則是分成左右或上下兩個區間。如上圖所示:S1把整個空間分成了左右兩個區間,左側區間中,又被S2橫向分割成了上下兩個區間,而S3又在S2的分割基礎上,將下部分分割成了左右兩個區間,….
如果已經存在一批Points,則較容易針對這批數據構建出一棵趨于平衡的K-D Tree,如若接收實時寫入的數據,或者說數據更新頻繁,K-D Tree則難以維持平衡態,除非對整棵樹進行重構。
Space Filling Curve
如果將一個完整的二維空間,分割成一個個大小相同的矩形,可以將Space Filling Curve簡單理解為它是一種將所有的矩形區域用一條線”串”起來的方法,因”串”的方式不同,也就引申出了不同的Space Filling Curve算法。
比較典型的如Z-Order Curve:
再如Hilbert Curve:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-43z6zHOE-1620871556108)(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==)]
Space Filling Curve事實上是為空間數據提供了一種一維排序的方法,它拉近了空間數據與傳統數據庫的距離:因為這意味著可以使用傳統的B-Tree或B±Tree索引,來存儲空間數據。
我們再借助于Z-Order的迭代構建過程,來加深讀者關于Space Filling Curve的理解:
如果將整個區間劃分成4個象限,第一輪迭代就是將這4個象限利用Z-Order曲線連接起來:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zTNW3nCz-1620871556108)(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==)]
而后,將第一輪迭代中的每一個象限,再進一步細分成4個象限,這樣共變成了16個區間。在第二輪迭代中,再將16一個小區間用Z-Order曲線連接起來:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-zlvUOgjf-1620871556110)(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==)]
第三輪迭代再進一步拆分、連接,變成下圖的樣子:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-DbbDkGxl-1620871556111)(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==)]
每一次迭代,都是在上一次迭代的”方格”基礎上,將每一個”方格”拆成了4個”子方格”,這種思想與Quad-Tree的思路是一致的,尤其是PR Quad-Tree,因此,也可將Space Filling Curve理解成一種高效構建Quad-Tree的方式,但需要說明的一點是,Z-Order論文的發布時間要遠早于Quad-Tree論文的時間。
GeoHash
GeoHash可以將Point編碼成一個字符串,如:
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-vke3Ejyf-1620871556112)(data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVQImWNgYGBgAAAABQABh6FO1AAAAABJRU5ErkJggg==)]
http://geohash.org這個網站提供了經緯度與Geohash的在線轉換能力。從編碼結果來看,可以發現三個特點:
越高的精度,字符串越長,可對比上表中的P1到P3的Geohash值變化。
P3所表示的區域涵蓋了P2,而P2涵蓋了P1,這層關系,在生成的Geohash值中這樣體現:
“ws10rn6y”(對應P2)以”ws10rn”(對應P3)為前綴
“ws10rn6yp9ms”(對應P1)以”ws10rn6y”(對應P2)為前綴。
位置相近的點,生成的Geohash值也相似,如P2與P4,兩者緯度相同,經度僅差0.001,在生成的Geohash值中僅最后一個字符有區別。
GeoMesa官方資料中,也給出了這樣一個典型的例子:
針對 (-78.48, 38.03),編碼的過程,如下:
第1步:經度二分
規則:先將完整的坐標系統(-180.000 -90.000, 180.000 90.000)按經度中點0.000進行二分,如果處于左邊則編碼為0,處于右邊,則編碼為1。
結果:因 (-78.48, 38.03)處于左側區間(-180.000, 0.000),因此,實際編碼為0。此時, (-78.48, 38.03)所處的區間被縮小為(-180.000 -90.000, 0.000 90.000)。
第2步:緯度二分
規則:基于第1步的結果,再將(-180.000 -90.000, 0.000 90.000)按緯度中點0.000進行二分,如果處于上側則編碼為1,處于下側,則編碼為0。
結果:因 (-78.48, 38.03)處于上側區間(-180.000, 0.000),編碼為1,與第一步的結果結合在一起,編碼變為”01″。此時, (-78.48, 38.03)所處的區間被縮小為(-180.000 0.000, 0.000 90.000)。
第3步:經度二分
規則:基于第2步的結果,再將(-180.000 0.000, 0.000 90.000)按經度中點-90.000進行二分,同樣,如果處于左側則編碼為0,處于右側,則編碼為1。
結果:因 (-78.48, 38.03)處于上側區間(-90.000, 0.000),編碼為1,與前兩步的結果結合在一起,編碼變為”011″。此時, (-78.48, 38.03)所處的區間被縮小為(-90.000 0.000, 0.000 90.000)。
第4步:緯度二分
……
就這樣,經度緯度不斷交替二分迭代,每一步的輸出結果如下表所示:
關于上表中的Bounding Boxes的不斷編碼,可直觀的體現在下圖中:
通過Base-32 Encoding,可以將上表中的編碼結果進一步編碼成字符串。例如,基于Base-32編碼規則:
因此,(-78.48, 38.03)的編碼為01100 10110 01010 00000 10110,將其轉換成對應的字符:
? 01100 -> d
? 10110 -> q
? 01010 -> b
? 00000 -> 0
? 10110 -> q
連在一起,(-78.48, 38.03)的最終編碼結果變為:dqb0q
經Geohash編碼后,其順序與Z-Order編碼保持一致,因此,也可以將Geohash是針對Z-Order算法的一種編碼機制。Geohash編碼帶來的顯著優勢是:以字符串字典順序表達Z-Order順序,利用字符串的前綴匹配規則,也可快速實現多邊形區域的重疊計算,但在編碼效率上卻并無任何優勢,如sfcurve項目中提供的Z2編碼算法,性能要遠高于Geohash算法。
哪種時空索引可應用于HBase?
HBase基于LSM-Tree架構,底層HFile文件以B+ Tree形式組織。在不影響HBase現有架構的基礎上,我們來分別探討一下各種時空索引與HBase結合的可能性。
從R-Trees的原始論文描述來看,它的設計是為了充分利用Disk Page的優勢,這一點與B-Tree類似。它支持良好的數據隨機寫入能力,能夠自平衡,從設計上來看,非常適合于矩形/多邊形空間對象的存儲。既然是致力于磁盤上的索引設計,對HBase現有架構帶來較大的侵入性,即使能對HBase進行改造,與B-Tree類似的這種設計,已經被證明難以支撐高吞吐量數據寫入。如果不做任何改造,我們只能考慮如何將R-Trees的數據映射到HBase的KeyValue模型中:HBase的數據按RowKey排序,從而能夠按照RowKey快速檢索,如果將R-Trees的數據以HBase原生數據形態組織,自然希望能提供一種方法,使得R-Trees中的數據排序方式與RowKey的排序一致,或者說,如何為R-Trees中的數據對象找到一種RowKey生成機制,使得R-Trees中的數據順序就是RowKey的字典順序。但我們知道,R-Trees中,其實已經弱化了數據對象”排序”的含義,一個節點與子節點之間,更多是一種包含于被包含的關系,一個空間對象可能歸屬于不同的分枝,這取決于那種劃分方案更優,當一個Leaf Node承載的對象過多時,也可能出現Split。如果按照傳統的樹遍歷的順序來理解R-Trees中的對象排序,這種排序是不確定的。這意味著,我們無法構建出一種確定順序的RowKey。
再來看一下K-D Trees:K-D Trees是針對高維點向量而設計,用來存儲二維的Point數據,自然也能很好的應對。K-D Trees的結構,與數據的寫入順序強相關,也可以說,同樣的一批數據,因順序不同,所構成的樹的結構截然不同,這個問題也存在于Point Quad Trees中,因樹結構的不確定性,它們與HBase結合的最大障礙,依然在于如何將Trees中的數據映射到KeyValue模型中。
PR Quad Tree的分區則不依賴于數據本身,更與數據的寫入順序無關,但PR Quad Tree限制每一個Point都獨占一個小方格的設計,使得每一個Point可能處在樹的不同層次/高度中,而且隨著數據的不斷寫入,同一個Point所處的高度可能會發生變化:如原來某一個方格中只有一個Point,但如果有新的數據寫入后,這個方格需要進一步被分拆成4個新的子方格,原來的Point在樹中的位置也發生了變化,因此,PR Quad Tree也無法直接應用于HBase。
如果對PR Quad Tree進行稍加改造:
這樣,帶來了三點”確定性”:樹的層次是確定的,每一個小方格在樹中的訪問路徑是確定的,每一個Point所屬的小方格也是確定的。因此,只要我們存在一種方法,將每一個小方格的訪問路徑,映射成HBase的RowKey,就完美解決了時空Points數據存放在HBase中的問題,這種思路正是Spatial Filling Curve的核心思想。
我們提到了兩種常見的Spatial Filling Curve: Z-Order Curve與Hilbert Curve。評價一種Spatial Filling Curve的優劣,有兩個關鍵的維度:
Z-Order曲線在距離保留度上,略弱于Hilbert曲線,但在編碼復雜度上,卻比Hilbert曲線更低,這是GeoMesa選擇Z-Order曲線的關鍵原因。
References
R-trees: A Dynamic Index Structure for Spatial Searching
The R±Tree: A Dynamic Index For Multi-Dimensional Objects
https://www.geomesa.org/documentation/
https://github.com/davidmoten/rtree
https://en.wikipedia.org/wiki/Z-order_curve
https://www.slideshare.net/shakil0304003/b-tree-rtree
https://en.wikipedia.org/wiki/Quadtree
https://github.com/davidmoten/rtree
https://github.com/varunpant/Quadtree
Comparison of Advance Tree Data Structures
Space-Filling Curves An Introduction
A dive into spatial search algorithms
James Fogarty: K-D Trees and Quad Trees
https://en.wikipedia.org/wiki/Space-filling_curve
http://geohash.org
https://github.com/geomesa/geomesa-tutorials
https://en.wikipedia.org/wiki/Geohash
Spatial Keys – Memory Efficient GeoHashes
GeoServer: CQL And ECQL
GeoServer: ECQL Reference
GeoMesa: Scaling up Geospatial Analysis
總結
以上是生活随笔為你收集整理的HBase与时空索引技术的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【收藏】HDFS的Java API使用
- 下一篇: IDEA 2021.1.2中scala生