GeoMesa原始碼學習:空間索引
分散式空間索引可以說是GeoMesa的靈魂了,它直接決定了空間資料的:(1)行主鍵(2)資料分割槽與負載均衡(3)索引高效查詢。所以說要想真正瞭解GeoMesa的核心程式碼,必須要把索引這一部分弄懂吃透。空間索引方法是一套完整切複雜的理論體系,建議讀者在閱讀本文之前先看這篇論文:
Böhm, Klump, and Kriegel. “XZ-ordering: a space-filling curve for objects with spatial extension.” 6th. Int. Symposium on Large Spatial Databases (SSD), 1999, Hong Kong, China. (http://www.dbs.ifi.lmu.de/Publikationen/Boehm/Ordering_99.pdf
下面進入正文。
1. 空間填充曲線(Space Filling Curve)
基於空間目標排序的索引方法的基本思想是按照某種策略將索引空間細分為許多均等的網格,並給每一網格分配一個編號,然後用這些編號為空間目標獲得一具有代表意義的數字。這樣,多維空間目標就可以背影射成一維的目標,從而也就可以使用現有的資料庫管理系統中比較成熟的一維索引技術來提高對空間資料的快速查詢和存取。問題的關鍵在於影射演算法必須較好地保持多維空間目標間的臨近關係以提供較好的空間查詢效能。空間填充曲線(後面簡稱sfc)是一種降低空間維度的方法。sfc能夠將高維空間中的資料對映到一維空間,使用經典線性索引結構儲存資料。空間目標排序的技術很多,最常見的sfc是基於Z排序(Z-order)和Hilbert排序的空間索引技術,技術的區別主要在於建立的多維到一維的對映關係是否能夠很好的保持多維空間目標間的臨近關係。關於Z-order和Hilbert曲線本文不做詳細闡述,請讀者自己查閱相關文獻。
Z-order:
如圖所示,我們將二進位制編碼的結果填寫到空間中,當將空間劃分為四塊時候,編碼的順序分別是左下角00,左上角01,右下腳10,右上角11,也就是類似於Z的曲線,當我們遞迴的將各個塊分解成更小的子塊時,編碼的順序是自相似的(分形),每一個子快也形成Z曲線,這種型別的曲線被稱為Peano空間填充曲線。
Hilbert曲線:
Z-Curve sfc可以將多維的空間資料降為一維,空間內某個區域的點的Z 序值可通過其位置計算出來,Z-Curve 空間填充曲線將地理空間按Z 字形劃分成不同的區域,同一區域內的空間物件Z 序值相等。Z-Curve 曲線定義為d 維空間Rd與一維空間I 之間的一一對映,其可以記作 Z:Rd→I。若點 p∈Rd,則象Z(p)∈I,也將象Z(p)稱為點 p 的 Z值。d 維m 階Z-Curve 曲線的對映演算法對應的虛擬碼如下所述。
在d 維m 階Z-Curve 曲線的對映演算法中外層迴圈for 的迴圈次數為m,內層迴圈for 的迴圈次數為d,則二重迴圈的執行次數為md,故d 維m 階Z 曲線的對映演算法的時間複雜度為O(md)。儲存點p 需要長度為d 的陣列,故d 維空間索引m 階Z-Curve 曲線的對映演算法的空間複雜度為O(d)。下圖給出HBase in Action中的一個例子:
注意:偶數位放經度,奇數位放緯度。
2. GeoMesa空間索引技術
GeoMesa使用了基於Z-order的空間索引技術。具體實現請參見locationTech另外一個sfcurve工程。使用IntelliJ開啟sfcurve工程,可以看到主要包含三個模組: api、hilbert以及zorder。api中定義了sfc操作介面,zorder與hilbert分別是api中各個介面的基於Z曲線與Hilbert曲線的實現。
首先我們分析一下api模組。api模組中一共包含三個類:SpaceFillingCurve2D、SpaceFillingCurveProvider以及SpaceFillingCurves。SpaceFillingCurve2D是一個trait,其中定義了所有sfc類需要實現的介面,其中toIndex是獲取z值,toPoint是根據z值獲取座標,toRanges是根據MBR獲取sfc所有被包含的range集合,用於row key掃描查詢。
SpaceFillingCurveProvider提供了一個獲取SpaceFillingCurve2D的工廠方法:build2DSFC。
SpaceFillingCurves類中包含了使用SpaceFillingCurveProvider獲取SpaceFillingCurve2D的標準業務邏輯:
基於api,sfcurve提供了zorder與hilbert兩種實現,下面我們再來分析一下geomesa中使用的zorder的實現,開啟zorder目錄如下:
在本模組中,ZOrderSFCProvider實現了api中的SpaceFillingCurveProvider,ZCurve2D實現了實現了api中的SpaceFillingCurve2D,通過ZCurve2D可以獲取Z-order sfc的兩種具體實現類:Z2與Z3,Z2為二維Z曲線,僅僅針對空間,Z3為三維Z曲線,同時考慮了空間與時間,它們有個共同的基類ZN,N代表維數:
再瞭解了sfcurve中sfc的整體架構之後,我們再來分析一下z曲線的具體實現。
3. SFCurve中Z2的實現:
下面我們來看看sfcurve是如何實現Z2索引的。開啟Z2類,如下圖所示。Z2中有一個建構函式apply(x:Int,y:Int)。sfcurve中使用了一個比較巧妙的演算法實現了GeoHash空間編碼,即首先將lat與lon根據使用者設定的精度轉換為int型(呼叫normalize方法),生成對應的x:Int與y:Int,然後分別對x與y呼叫split函式,也就是將前31位的相鄰兩位之間插入0,最後對split(y)左移一位然後與split(x)取並,即得到對應的z-order曲線上的值(因為根據geohash的定義,偶數位放經度,奇數位放緯度)。
GeoMesa中並沒有直接使用sfcurve中的SpaceFillingCurve2D,而是提供了geomesa-z3_2.11模組,它基於sfcurve工程zorder模組提供了一些了sfc相關的工具類,如下圖:
SpaceFillingCurve是GeoMesa所有空間填充曲線的基類,其子類Z2SFC對應的就是Z2:
我們接下來分析一下Z2SFC是如何生成Z-order code的:
首先index方法會將經緯度進行一下normalize,轉換成int型別,然後呼叫前面我們分析的sfcurve中的Z2的apply(x:Int,y:Int)方法然後構造Z2例項,GeoMesa就是通過返回的Z2物件例項計算具體Z-order code值得的