1. 程式人生 > 其它 >百萬級要素優化:基於PG與PostGIS的實時向量瓦片服務

百萬級要素優化:基於PG與PostGIS的實時向量瓦片服務

背景介紹

對於某些應用場景,資料量很容易達到百萬級要素,資料每天都在改變。而且還要考慮在1-6級這種小級別時候的資料展示,那麼這個時候僅僅是PG與PostGIS簡單的向量瓦片服務比較困難達到客戶想要的效果,這個時候就要考慮做一些優化了。

優化方法

百萬級要素優化對於不同的業務來說,有相應不同的解決辦法,沒有最好,只有更好,這個時候要做考量,下面介紹幾個常用的會考慮使用的方法:

1、依據業務,對要素進行抽稀過濾

根據業務層面,在不同場景瀏覽不同的要素,雖然總的要素在幾百萬,但是這個時候過濾一部分後剩餘要素可以進行實時向量瓦片展示。

2、幾種抽稀過濾的演算法

除第一種方法外,其餘幾種方法包含一些個人的想法與思路,其它小夥伴有其他好的思路,或者對我的想法思路有疑問的話,歡迎在評論區留言。

1)道格拉斯-普客演算法(DP)

該演算法個人理解為是簡化的演算法,對於線或者面的邊界,可以使用該演算法對節點進行抽稀,達到對線或者面的簡化。但是為什麼在點要素抽稀過濾中也介紹該演算法呢?

答案:在軌跡資料中,每一次儲存的都是點位資訊(點),所以對於該種資料可以使用該演算法進行過濾,但是對於一個點即代表一種資訊的資料來說,該演算法不太適用。

2)基於網格的抽稀過濾演算法

該演算法結果為均勻分佈。

  1. 根據資料範圍以及閾值將範圍劃分網格。
  2. 遍歷網格,利用網格與資料求交(使用PostgreSQL的gist索引,加快速度),計算交到的要素。
  3. 將交到要素隨機選取一個,其餘要素捨棄。
  4. 遍歷完網格後,即可完成要素過濾。

本人寫了一個簡單的實現,供各位參考。

--drop FUNCTION point_simplify_grid;
CREATE
OR REPLACE FUNCTION point_simplify_grid (
    PAR_minx DOUBLE PRECISION,
    PAR_miny DOUBLE PRECISION,
    PAR_maxx DOUBLE PRECISION,
    PAR_maxy DOUBLE PRECISION,
    PAR_threshold DOUBLE PRECISION
) RETURNS geometry AS $$
DECLARE REC_points RECORD ; ARR_result_points geometry []
; GEO_point geometry ; grid_key VARCHAR (10) ; GEOM_result geometry ; grid_num_x INTEGER ; grid_num_y INTEGER ; grid_json jsonb ; BEGIN grid_num_x := CEIL ( (PAR_maxx - PAR_minx) / PAR_threshold ) ; grid_num_y := CEIL ( (PAR_maxy - PAR_miny) / PAR_threshold ) ; grid_json := '{}' :: jsonb ; ARR_result_points := '{}' :: geometry []; FOR REC_points IN SELECT * FROM tycd_view WHERE geom && st_makeenvelope ( PAR_minx, PAR_miny, PAR_maxx, PAR_maxy, 4326 ) LOOP grid_key := CEIL ( ( st_x (REC_points.geom) - PAR_minx ) / PAR_threshold ) || ',' || CEIL ( ( st_y (REC_points.geom) - PAR_miny ) / PAR_threshold ) ; IF NOT (grid_json ? grid_key) THEN ARR_result_points := array_append( ARR_result_points, st_setsrid (REC_points.geom, 4326) ) ; grid_json := grid_json || ('{"' || grid_key || '":true}') :: jsonb ; END IF ; END loop ; GEOM_result := st_union (ARR_result_points) ; RETURN GEOM_result ; END ; $$ LANGUAGE plpgsql; --select st_asgeojson(point_simplify_grid(73,3,135,53,0.2))

github連結:https://github.com/MrSmallLiu/point_dilution

3)基於距離的抽稀過濾演算法

該演算法結果為均勻分佈。

  1. 設定距離閾值。
  2. 選取初始點,計算其它點到該點的距離,閾值範圍內的點,捨棄。
  3. 從上次結果中,選取另一個點作為基準點,再次執行(2)的步驟,只需計算保留下來的點。
  4. 全部點計算完成後,即可完成要素抽稀過濾。

4)基於隨機數的抽稀過濾演算法

該演算法結果可以保持資料疏密。

對於大資料量時的隨機數,可以達到偽隨機性,所以可以利用該演算法進行隨機性的抽稀。

  1. 設定經驗閾值,例如r=0.5,同時因為該演算法效率可以實時進行,可以考慮基於每一次查詢向量切片時,根據級別設定閾值。
  2. 查詢的sql語句中或者其它方式後面新增where條件,where random()>0.5時保留要素。
  3. 基於以上就可以完成50%的抽稀率。

使用的侷限:

  1. 對於小資料量可能效果達不到。

優勢:

  1. 效率很高,可以不用提前處理資料。
  2. 可以將random()做到資料欄位中,使用btree加速。
  3. 設定欄位中後,可以保證例如小級後的結果例如3級,在後續級別中保留例如4-18級。

5)基於網格與隨機數抽稀過濾演算法

該演算法結果可以保持資料疏密。

  1. 根據範圍與網格大小閾值,劃分資料網格。
  2. 遍歷網格,利用網格與資料求交(使用PostgreSQL的gist索引,加快速度),計算交到的要素。
  3. 在網格內根據隨機數,將網格內資料隨機丟棄一些。
  4. 網格遍歷完成後,即完成資料抽稀過濾。

該演算法彌補了單純基於資料數的一些侷限性,不會導致資料存在缺塊(對於大資料量,可能性微乎其微)。

6)基於距離並保持疏密程度的抽稀過濾演算法

該演算法結果可以保持資料疏密。

  1. 設定距離閾值,設定丟棄比例。
  2. 選取初始點,遍歷其它要素,計算其它要素到該要素的距離,小於距離閾值的歸為一片。
  3. 將該片資料,基於丟棄比例,捨棄一部分要素。
  4. 繼續下一個點為基準點,迴圈(2)(3)的步驟。
  5. 全部要素完成後,即可完成資料抽稀過濾。

以上即為介紹的幾種抽稀過濾演算法,可以在合適的場景使用合適的方式。其中雖然有些演算法存在侷限性(基於隨機數的演算法),但是對於大資料量來說,可能性很低,並且效率極高,可以做到實時,以下效果也是採用該方法。

效果展示

示例:

  • 資料量:本示例資料量為300萬點要素;
  • 抽稀過濾方法:基於隨機數抽稀過濾,將random欄位新增到欄位中,同時利用該方法結合經驗值,實時抽稀過濾展示(例如:<4級:0.2,4-6級:0.4,7-9:0.7,>10不抽稀過濾);
  • 資料展示方法:實時向量切片,結合根據級別抽