閱讀955 返回首頁    go 阿裏雲 go 技術社區[雲棲]


無人駕駛、配送機器人背後的技術 - PostGIS點雲(pointcloud)應用實踐

標簽

PostgreSQL , PostGIS , box , grid , pointcloud , pgpointcloud , point聚合 , KNN , 自動駕駛 , 自動配送 , 無人駕駛


背景

科幻電影的場景隨著技術的發展,正在一步步的從熒幕變成現實。比如汽車廠商、科技公司在嚐試的無人駕駛。

無人駕駛應用是非常廣泛的,比如快遞行業,時機成熟以後,將來可能快遞員這個職業也會逐漸從社會上消失(解放快遞員的雙手和創造力,讓更多的人參與到科技改變生活當中)。

無人駕駛,少不了導航,導航少不了位置信息,位置信息又是由無數的點組成的。點越多,精細度越高,就像照片一樣,以前的手機像素和感光元件很粗糙,拍攝的照片清晰度就不如現在的手機。

要達到無人駕駛的可用性,對於點的密度也是有要求的,比如,每平方米一個點的話,1平方公裏就是100萬個點。1米顯然也是一個比較粗糙的數據,但是還好,如果隻是駕駛,不需要覆蓋到所有的點,隻需要覆蓋到道路所占麵積即可。

比如雙向6車道,寬度25米的道路,100公裏需要多少個點來描述呢? 如果每平方米一個點,需要250萬個點,如果每平方米再劃分10個格子,那麼需要2500萬個點,如果每平方米劃分成100個格子,那麼需要2.5億個點。(使用PostgreSQL PostGIS可以做到更精細,使用point經緯度表示(float8,float8),並非格子表示。有些數據庫使用geohash表示,精確度受限。有興趣的童鞋可以參考 《geohash vs PostGIS》 )

大概的point體量知道了之後,如何與無人駕駛結合起來呢?

為了方便描述,我們把point點陣稱為點雲(pointcloud)。當設定了無人駕駛的起始點和路徑,需要將涉及道路的點都載入汽車,每個點除了位置,還應該有其他屬性,比如窨井蓋、坑、水泥路OR瀝青路、紅綠燈、硬路肩等,這些屬性可以使用其他字段存儲。

點雲與自動駕駛軟件結合使用。

一、使用什麼存儲點雲

PostGIS是一個非常流行的GIS數據管理插件,在天文、科研、軍工、互聯網應用廣泛,阿裏雲RDS for PostgreSQL集成了PostGIS。你可以選擇RDS for PostgreSQL或者自己安裝PostgreSQL+PostGIS。

我們可以將數據存儲在PostgreSQL中,數據類型使用geometry,索引使用GiST,檢索性能杠杆的。

二、建模

pic

每條道路由若幹個點組成,車輛啟動時,定位,並載入附近的點,返回與道路的多邊形相交的點。要做到無人駕駛,點雲的分辨率(密度)要求很高,前麵講了,雙向6車道,寬度25米的道路,每平方米使用100個點表示時,100公裏需要2.5億個點。

如果無人駕駛的車輛運行速度為1公裏每分鍾(相當於時速60公裏),那麼點的載入速度至少要達到250萬點/分鍾。

我們先使用每條記錄表示一個點(後麵再來談優化)。

1. 表結構(以RDS for PostgreSQL舉例)

create extension postgis;  -- 創建postgis插件  
  
postgres=# create table cloudpoint_test(  
  id serial primary key,  -- 主鍵  
  loc geometry,        -- 經緯度(或point)  
  other text           -- 其他屬性  
);  
CREATE TABLE  

2. GIST索引

postgres=# create index idx on cloudpoint_test using gist(loc) with (buffering=on);  
CREATE INDEX  

三、點雲的數據寫入速度

1. 灌入測試數據,50個並發,灌入1億測試數據。(隨機點的邊界是橫豎10000,覆蓋1一個點。)

vi ins.sql  
  
insert into cloudpoint_test (loc,other) values (st_makepoint(random()*10000, random()*10000) , 'test');  

2. 灌入數據性能指標,每秒灌入約 16.6 萬條記錄。

pgbench -M prepared -n -r -P 1 -f ./ins.sql -c 50 -j 50 -t 2000000  
  
transaction type: Custom query  
scaling factor: 1  
query mode: prepared  
number of clients: 50  
number of threads: 50  
number of transactions per client: 2000000  
number of transactions actually processed: 100000000/100000000  
latency average: 0.298 ms  
latency stddev: 0.854 ms  
tps = 166737.438650 (including connections establishing)  
tps = 166739.148413 (excluding connections establishing)  
statement latencies in milliseconds:  
        0.297896        insert into cloudpoint_test (loc,other) values (st_makepoint(random()*10000, random()*10000) , 'test');  

四、點雲搜索設計

為了達到最好的性能,建議參考如下文章,原理請感興趣的童鞋打開了細讀

《GIS附近查找性能優化 - PostGIS long lat geometry distance search tuning using gist knn function》

1. 搜索point函數如下

create or replace function ff(geometry, float8, int) returns setof record as $$                                                          
declare  
  v_rec record;  
  v_limit int := $3;  
begin  
  set local enable_seqscan=off;   -- 強製索引, 掃描行數夠就退出.  
  for v_rec in   
    select *,  
    ST_Distance ( $1, loc ) as dist   
    from cloudpoint_test   
    order by loc <-> $1           -- 按距離順序由近到遠返回  
  loop  
    if v_limit <=0 then           -- 判斷返回的記錄數是否達到LIMIT的記錄數  
      raise notice '已經取足limit設置的 % 條數據, 但是距離 % 以內的點可能還有.', $3, $2;  
      return;  
    end if;  
    if v_rec.dist > $2 then       -- 判斷距離是否大於請求的距離   
      raise notice '距離 % 以內的點已輸出完畢', $2;  
      return;  
    else  
      return next v_rec;  
    end if;  
    v_limit := v_limit -1;  
  end loop;  
end;  
$$ language plpgsql strict volatile;  

2. 搜索SQL用法

搜索距離st_makepoint(1500,1500)在100以內,按距離由近到遠,返回最多10000條記錄(limie 10000)。

postgres=# select * from ff(st_makepoint(1500,1500), 100, 10000) as t (id int, loc geometry, other text, dist float8);  
NOTICE:  已經取足limit設置的 10000 條數據, 但是距離 100 以內的點可能還有.  
    id     |                    loc                     | other |       dist          
-----------+--------------------------------------------+-------+-------------------  
  54528779 | 01010000000000EFF6307297400000010D306E9740 | test  | 0.710901366481036  
  52422694 | 01010000000080EE51B171974000003DE6256D9740 | test  | 0.829108575682196  
  20123322 | 0101000000000074AD5C6F97400000C766CE739740 | test  |   0.9648380442046  
  58784192 | 010100000000803A65F4749740008012FDD8709740 | test  |  1.25666215808279  

五、點雲的搜索速度

1. 搜索st_makepoint(5000,5000)附近距離1000以內的20萬個點,按距離由近到遠返回。

1秒。

explain (analyze,verbose,timing,costs,buffers) select * from ff(st_makepoint(5000,5000), 1000, 200000) as t(id int, loc geometry, other text, dist float8);  
  
NOTICE:  已經取足limit設置的 200000 條數據, 但是距離 1000 以內的點可能還有.  
                                                       QUERY PLAN                                                         
------------------------------------------------------------------------------------------------------------------------  
 Function Scan on public.ff t  (cost=0.25..10.25 rows=1000 width=76) (actual time=917.748..945.838 rows=200000 loops=1)  
   Output: id, loc, other, dist  
   Function Call: ff('0101000000000000000088B340000000000088B340'::geometry, 1000::double precision, 200000)  
   Buffers: shared hit=201288, temp read=1418 written=1417  
 Planning time: 0.057 ms  
 Execution time: 959.534 ms  
(6 rows)  

2. 搜索st_makepoint(5000,5000)附近距離2000以內的100萬個點,按距離由近到遠返回。

10秒。

explain (analyze,verbose,timing,costs,buffers) select * from ff(st_makepoint(5000,5000), 2000, 1000000) as t(id int, loc geometry, other text, dist float8);  
  
NOTICE:  已經取足limit設置的 1000000 條數據, 但是距離 2000 以內的點可能還有.  
                                                        QUERY PLAN                                                           
---------------------------------------------------------------------------------------------------------------------------  
 Function Scan on public.ff t  (cost=0.25..10.25 rows=1000 width=76) (actual time=8867.007..9006.401 rows=1000000 loops=1)  
   Output: id, loc, other, dist  
   Function Call: ff('0101000000000000000088B340000000000088B340'::geometry, 2000::double precision, 1000000)  
   Buffers: shared hit=1006220, temp read=7082 written=7081  
 Planning time: 0.059 ms  
 Execution time: 9074.267 ms  
(6 rows)  

3. 搜索st_makepoint(5000,5000)附近距離3000以內的500萬個點,按距離由近到遠返回。

43秒。

explain (analyze,verbose,timing,costs,buffers) select * from ff(st_makepoint(5000,5000), 3000, 5000000) as t(id int, loc geometry, other text, dist float8);  
  
NOTICE:  已經取足limit設置的 5000000 條數據, 但是距離 3000 以內的點可能還有.  
                                                         QUERY PLAN                                                            
-----------------------------------------------------------------------------------------------------------------------------  
 Function Scan on public.ff t  (cost=0.25..10.25 rows=1000 width=76) (actual time=42168.038..42861.462 rows=5000000 loops=1)  
   Output: id, loc, other, dist  
   Function Call: ff('0101000000000000000088B340000000000088B340'::geometry, 3000::double precision, 5000000)  
   Buffers: shared hit=5030448, temp read=35402 written=35401  
 Planning time: 0.060 ms  
 Execution time: 43201.879 ms  
(6 rows)  

現在看起來性能還不錯對吧?但是優化是沒有止境的,所以還有優化空間,有興趣的話,可以繼續請往下看。

六、點雲搜索瓶頸分析

瓶頸分為3個方麵

1. 函數回調

由於需要返回的記錄數非常多,PostgreSQL的返回記錄回調也非常多,導致了較大開銷。詳見:

《分析加速引擎黑科技 - LLVM、列存、多核並行、算子複用 大聯姻 - 一起來開啟PostgreSQL的百寶箱》

pic

pic

pic

使用perf可以觀察

perf record -ag -p $PID  
  
perf report -ag --stdio  

2. 離散掃描IO放大

由於點位數據可能是隨機采錄的,所以在堆存儲層麵沒有順序可言,同時搜索某個點附近的點時,也沒有順序可言,離散的IO請求導致了HEAP IO放大。原理詳見:

《索引順序掃描引發的堆掃描IO放大背後的統計學原理與解決辦法》

3. GiST索引離散度品質

GiST索引構建時,也有索引條目的離散度的問題,參考:

https://www.postgresql.org/docs/10.0/static/gist-implementation.html

Beginning in version 9.2,   
PostgreSQL supports a more efficient method to build GiST indexes based on buffering,   
which can dramatically reduce the number of random I/Os needed for non-ordered data sets.   
  
For well-ordered data sets the benefit is smaller or non-existent,   
because only a small number of pages receive new tuples at a time,   
and those pages fit in cache even if the index as whole does not.  

以上三個問題如何優化呢?

七、點雲搜索優化

1. get next tuple回調優化

回調太多,返回或處理非常多記錄時可能有瓶頸,有3種優化方法。

1 聚合

前麵我們精細化到了每平米100個點(100條記錄),如果我們把每平米(作為一個格子),將這100條記錄聚合為1條記錄,那麼記錄的總數將下降100倍。

1億記錄下降到100萬,用戶請求的記錄數也下降100倍。

pic

聚合後的表結構設計如下

postgres=# create table cloudpoint_test_agg(  
  id serial primary key,        -- 主鍵  
  loc_box geometry,          -- 格子(閉合多邊形), 表示一個範圍內的點  
  loc_agg geometry[],        -- 經緯度數組(或point數組)  
  other_agg text[]           -- 其他屬性數組  
);  
CREATE TABLE  
  
create index idx_cloudpoint_test_agg_1 on cloudpoint_test_agg using gist(loc_box) with (buffering=on);  
如何聚合?

首先將道路多邊形網格化處理,轉換為相鄰的六邊形網格,參考如下方法:

《蜂巢的藝術與技術價值 - PostgreSQL PostGIS's hex-grid》

轉換後的道路信息如下

create table cloudpoint_test_grid(  
  id serial primary key,    -- 六邊形ID  
  loc_box geometry     -- 單個六邊形  
);  

將網格與點雲數據JOIN,並聚合為聚合後的數據。

insert into cloudpoint_test_agg (loc_box,loc_agg,other_agg)  
  select t1.loc_box, array_agg(t2.loc) as loc_agg, array_agg(t2.other) as other_agg from   
    cloudpoint_test_grid t1 join cloudpoint_test t2  
    on (ST_Contains(t1.loc_box, t2.loc)) group by t1.loc_box;   

例子

本例使用標準正方形進行網格化,((0,0),(10000,10000)) 區域的網格化操作如下

每100*100一個格子。

pic

https://postgis.net/docs/manual-2.3/ST_MakeBox2D.html

do language plpgsql $$  
declare  
  x int;  
  y int;  
begin  
  for x in select generate_series(0,10000,100) loop  
    for y in select generate_series(0,10000,100) loop  
      insert into cloudpoint_test_grid(loc_box) values (ST_MakeBox2D( st_makepoint(x,y), st_makepoint(x+100, y+100) ));  
    end loop;  
  end loop;  
end;  
$$;  
postgres=# select * from cloudpoint_test_grid;  
  id   |                                                                                          loc_box                                                                                             
-------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------  
     1 | 010300000001000000050000000000000000000000000000000000000000000000000000000000000000005940000000000000594000000000000059400000000000005940000000000000000000000000000000000000000000000000  
     2 | 010300000001000000050000000000000000000000000000000000594000000000000000000000000000006940000000000000594000000000000069400000000000005940000000000000594000000000000000000000000000005940  
     3 | 010300000001000000050000000000000000000000000000000000694000000000000000000000000000C0724000000000000059400000000000C072400000000000005940000000000000694000000000000000000000000000006940  
     4 | 0103000000010000000500000000000000000000000000000000C07240000000000000000000000000000079400000000000005940000000000000794000000000000059400000000000C0724000000000000000000000000000C07240  
  
......  

生成聚合數據

insert into cloudpoint_test_agg (loc_box,loc_agg,other_agg)  
  select t1.loc_box, array_agg(t2.loc) as loc_agg, array_agg(t2.other) as other_agg from   
    cloudpoint_test_grid t1 join cloudpoint_test t2  
    on (ST_Contains(t1.loc_box, t2.loc)) group by t1.loc_box;   
  
INSERT 0 10000  
  
select count(*) from cloudpoint_test_agg;  
10000  

從聚合後的點雲數據搜索附近點的函數如下

create or replace function ff1(geometry, float8, int) returns setof record as $$                                                          
declare  
  v_rec record;  
  v_limit int := $3;  
begin  
  set local enable_seqscan=off;   -- 強製索引, 掃描行數夠就退出.  
  for v_rec in   
    select *,  
    ST_Distance ( $1, loc_box ) as dist   
    from cloudpoint_test_agg   
    order by loc_box <-> $1           -- 按距離順序由近到遠返回  
  loop  
    if v_limit <=0 then           -- 判斷返回的記錄數是否達到LIMIT的記錄數  
      raise notice '已經取足limit設置的 % 條數據, 但是距離 % 以內的點可能還有.', $3, $2;  
      return;  
    end if;  
    if v_rec.dist > $2 then       -- 判斷距離是否大於請求的距離   
      raise notice '距離 % 以內的點已輸出完畢', $2;  
      return;  
    else  
      return next v_rec;  
    end if;  
    v_limit := v_limit - array_length(v_rec.loc_agg, 1);  -- 扣減grid內的point個數  
  end loop;  
end;  
$$ language plpgsql strict volatile;  
聚合後的搜索SQL例子

搜索距離st_makepoint(1500,1500)在100以內,按距離由近到遠,返回最多10000條記錄(limie 10000)。

postgres=# select * from ff1(st_makepoint(1500,1500), 100, 10000) as t (id int, loc_box geometry, loc_agg geometry[], other_agg text[], dist float8);  
聚合後的速度測試

1. 搜索st_makepoint(5000,5000)附近距離1000以內的20萬個點,按距離由近到遠返回。

15毫秒。

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from ff1(st_makepoint(5000,5000), 1000, 200000) as t (id int, loc_box geometry, loc_agg geometry[], other_agg text[], dist float8);  
NOTICE:  已經取足limit設置的 200000 條數據, 但是距離 1000 以內的點可能還有.  
                                                     QUERY PLAN                                                       
--------------------------------------------------------------------------------------------------------------------  
 Function Scan on public.ff1 t  (cost=0.25..10.25 rows=1000 width=108) (actual time=15.642..15.643 rows=20 loops=1)  
   Output: id, loc_box, loc_agg, other_agg, dist  
   Function Call: ff1('0101000000000000000088B340000000000088B340'::geometry, 1000::double precision, 200000)  
   Buffers: shared hit=465  
 Planning time: 0.082 ms  
 Execution time: 15.670 ms  
(6 rows)  

2. 搜索st_makepoint(5000,5000)附近距離2000以內的100萬個點,按距離由近到遠返回。

63毫秒。

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from ff1(st_makepoint(5000,5000), 2000, 1000000) as t (id int, loc_box geometry, loc_agg geometry[], other_agg text[], dist float8);  
NOTICE:  已經取足limit設置的 1000000 條數據, 但是距離 2000 以內的點可能還有.  
                                                     QUERY PLAN                                                        
---------------------------------------------------------------------------------------------------------------------  
 Function Scan on public.ff1 t  (cost=0.25..10.25 rows=1000 width=108) (actual time=63.287..63.296 rows=100 loops=1)  
   Output: id, loc_box, loc_agg, other_agg, dist  
   Function Call: ff1('0101000000000000000088B340000000000088B340'::geometry, 2000::double precision, 1000000)  
   Buffers: shared hit=2143  
 Planning time: 0.057 ms  
 Execution time: 63.322 ms  
(6 rows)  

3. 搜索st_makepoint(5000,5000)附近距離3000以內的500萬個點,按距離由近到遠返回。

312毫秒。

postgres=# explain (analyze,verbose,timing,costs,buffers) select * from ff1(st_makepoint(5000,5000), 3000, 5000000) as t (id int, loc_box geometry, loc_agg geometry[], other_agg text[], dist float8);  
NOTICE:  已經取足limit設置的 5000000 條數據, 但是距離 3000 以內的點可能還有.  
                                                      QUERY PLAN                                                         
-----------------------------------------------------------------------------------------------------------------------  
 Function Scan on public.ff1 t  (cost=0.25..10.25 rows=1000 width=108) (actual time=312.315..312.394 rows=501 loops=1)  
   Output: id, loc_box, loc_agg, other_agg, dist  
   Function Call: ff1('0101000000000000000088B340000000000088B340'::geometry, 3000::double precision, 5000000)  
   Buffers: shared hit=10729  
 Planning time: 0.077 ms  
 Execution time: 312.463 ms  
(6 rows)  

聚合前後的性能對比圖

pic

2 pgpointcloud

pgpointcloud是PostgreSQL中的一個存儲點雲數據(LIDAR)的插件,具有類似PostGIS raster數據的功能,同時還有更豐富的擴展功能。

https://github.com/pgpointcloud/pointcloud

pic

《LIDAR in PostgreSQL with PointCloud》

LIDAR傳感器,在掃描空間時可能快速的產生上百萬的點,每個點除了包含XYZ坐標值,還可能包含更多其他維度信息,例如時間、RGB值、其他屬性,還有可能返回的是一批點。

因此沒有固定的類型來存儲LIDAR傳感器上報的數據,pgpointcloud使用"schema document"類型來描述LIDAR傳感器上報的數據,格式與PDAL庫的標準一致。

每一個點包含若幹維度的信息,每個維度可能是任意數據類型,類似複合類型。

LIDAR sensors quickly produce millions of points with large numbers of variables measured on each point.   
  
The challenge for a point cloud database extension is efficiently storing this data while allowing high fidelity access to the many variables stored.  
  
Much of the complexity in handling LIDAR comes from the need to deal with multiple variables per point.   
  
The variables captured by LIDAR sensors varies by sensor and capture process.   
  
Some data sets might contain only X/Y/Z values.   
  
Others will contain dozens of variables: X, Y, Z; intensity and return number; red, green, and blue values; return times; and many more.   
  
There is no consistency in how variables are stored:   
intensity might be stored in a 4-byte integer, or in a single byte; X/Y/Z might be doubles, or they might be scaled 4-byte integers.  
  
PostgreSQL Pointcloud deals with all this variability by using a "schema document" to describe the contents of any particular LIDAR point.   
  
Each point contains a number of dimensions, and each dimension can be of any data type,   
with scaling and/or offsets applied to move between the actual value and the value stored in the database.   
  
The schema document format used by PostgreSQL Pointcloud is the same one used by the PDAL library.  

pgpointcloud是專為處理LIDAR數據而設計的插件,推薦使用。

3 LLVM(JIT)

由於被處理的記錄多,導致get next tuple的開銷可能被放大,解決辦法是降低回調的開銷,可以使用code gen,也可以使用向量計算的方法。

參考如下:

《PostgreSQL 10.0 preview 性能增強 - 推出JIT開發框架(朝著HTAP邁進)》

《分析加速引擎黑科技 - LLVM、列存、多核並行、算子複用 大聯姻 - 一起來開啟PostgreSQL的百寶箱》

《PostgreSQL 向量化執行插件(瓦片式實現) 10x提速OLAP》

性能提升參考以上文檔,向量計算有10倍提升,LLVM至少有2倍提升。

2. GiST索引優化

gist 索引優化,目的是減少碎片。使用GiST的buffering開關即可,建索引時可以指定。

create index idx_cloudpoint_test_agg_1 on cloudpoint_test_agg using gist(loc_box) with (buffering=on);  
create index idx_cloudpoint_test_1 on cloudpoint_test using gist(loc) with (buffering=on);  

3. 流式返回

由於返回的記錄較多,除了批量一次性返回,還有一種方法,流式的返回。

流式返回有兩種方法,一種是遊標,另一種是異步消息。

1. 遊標實現流式返回。

begin;  
  
declare cur1 cursor for select * from (select *, ST_Distance ( st_makepoint(5000,5000), loc ) as dist from cloudpoint_test order by st_makepoint(5000,5000) <-> loc ) t where dist < 1000;  
  
fetch 100 from cur1;  
  
fetch ...;  
  
-- 客戶端接收到足夠的數據,或者距離超出後,不再接收,關閉遊標,退出事務。  
  
close cur1;  
  
end;  

具體用法,參考編程語言對應的PostgreSQL驅動,例如jdbc for postgresql, libpq等。

2. 異步消息實現流式返回。

會話1,監聽通道

listen abcd;  

會話2,發起請求,往監聽通道發異步消息

create or replace function ff(geometry, float8, int, text) returns void as $$   
declare  
  v_rec record;  
  v_limit int := $3;  
begin  
  set local enable_seqscan=off;   -- 強製索引, 掃描行數夠就退出.  
  for v_rec in   
    select *,  
    ST_Distance ( $1, loc ) as dist   
    from cloudpoint_test   
    order by loc <-> $1           -- 按距離順序由近到遠返回  
  loop  
    if v_limit <=0 then           -- 判斷返回的記錄數是否達到LIMIT的記錄數  
      raise notice '已經取足limit設置的 % 條數據, 但是距離 % 以內的點可能還有.', $3, $2;  
      return;  
    end if;  
    if v_rec.dist > $2 then       -- 判斷距離是否大於請求的距離   
      raise notice '距離 % 以內的點已輸出完畢', $2;  
      return;  
    else  
      -- return next v_rec;  
      perform pg_notify ($4, v_rec::text);  
    end if;  
    v_limit := v_limit -1;  
  end loop;  
end;  
$$ language plpgsql strict volatile;  

會話2發起搜索請求

postgres=# select ff(st_makepoint(5000,5000), 1000, 10, 'abcd');  
NOTICE:  已經取足limit設置的 10 條數據, 但是距離 1000 以內的點可能還有.  
 ff   
----  
   
(1 row)  

會話1將異步收到通道發來的消息

Asynchronous notification "abcd" with payload "(38434407,01010000000060763E6E87B34000C0028CC587B340,test,0.613437682476958)" received from server process with PID 36946.  
Asynchronous notification "abcd" with payload "(41792090,0101000000006008B91F88B3400000D5D13B87B340,test,0.776283650707887)" received from server process with PID 36946.  
Asynchronous notification "abcd" with payload "(90599062,0101000000002057B2A888B34000C093516E88B340,test,0.787366330405518)" received from server process with PID 36946.  
Asynchronous notification "abcd" with payload "(69482516,01010000000000A574AE88B34000601AEBA888B340,test,0.948568992176712)" received from server process with PID 36946.  
Asynchronous notification "abcd" with payload "(12426846,0101000000006075D49188B34000E0E8E70487B340,test,1.13425697837729)" received from server process with PID 36946.  
Asynchronous notification "abcd" with payload "(98299759,0101000000004054059388B340006014ED1089B340,test,1.21096126708341)" received from server process with PID 36946.  
Asynchronous notification "abcd" with payload "(31175773,010100000000C03179EE88B34000A03E0C1B87B340,test,1.29136079279649)" received from server process with PID 36946.  
Asynchronous notification "abcd" with payload "(11651191,01010000000080C6634C87B34000E0A4852689B340,test,1.34753214416354)" received from server process with PID 36946.  
Asynchronous notification "abcd" with payload "(50248773,010100000000C064B3A686B34000809FA0F487B340,test,1.34955653568245)" received from server process with PID 36946.  
Asynchronous notification "abcd" with payload "(28170573,010100000000608F958B86B34000C051C1F587B340,test,1.45529948415963)" received from server process with PID 36946.  

具體用法,參考編程語言對應的PostgreSQL驅動,例如jdbc for postgresql, libpq等。

4. HEAP離散IO放大優化

在生成點雲數據時,按地理位置信息的順序進行數據的錄入堆表,那麼在搜索範圍數據時,可以縮小需要訪問的堆表數據塊。

就好像玩樂高積木一樣,我們可以購買一些小的分隔盒,把不同形狀的小零件放入不同的分隔盒,當需要找某個形狀的零件時,定位到小的盒子後,一直從這個小盒子裏拿就好了。而不需要到其他的小盒子裏拿。

這既是數據整理的魅力,將數據按查詢需求歸類存放,減少索引查詢時HEAP IO的開銷。

八、小結

無人駕駛、物流配送機器人等業務的興起,給傳統的GIS數據庫帶來了不小的挑戰。

1. 首先是數據量方麵,一條6車道的道路,每平方米打10個點的話,100公裏的道路,需要2.5億個點。

2. 其次是數據屬性,不僅僅要存儲經緯度,海拔(XYZ),可能還要存儲點的材質、密度、顏色、時間等屬性。不僅僅要存儲單點,可能還要存儲點陣,線段等等。

3. 最後的數據的寫入,以及大批量數據的查詢性能需求方麵,打點時要求快速的寫入,定位、自動駕駛時需要快速的獲取周邊的海量POINT。

PostgreSQL, PostGIS, pg-grid, pgpointcloud很好的滿足了這三個方麵的需求。

在優化代碼前,獲取20萬個點,可以1秒內返回。返回更大數據量時,可以通過pgpointcloud、網格化聚合等優化手段來提速,返回500萬個點,僅需312毫秒。

但是性能優化永遠是沒有止境的話題,加油小夥伴們,加油PostgreSQL,PostGIS, pgpointcloud。

九、參考

https://pointclouds.org/

https://s3.cleverelephant.ca/foss4gna2013-pointcloud.pdf

https://postgis.net/documentation/

《GIS附近查找性能優化 - PostGIS long lat geometry distance search tuning using gist knn function》

《分析加速引擎黑科技 - LLVM、列存、多核並行、算子複用 大聯姻 - 一起來開啟PostgreSQL的百寶箱》

《索引順序掃描引發的堆掃描IO放大背後的統計學原理與解決辦法 - PostgreSQL index scan enlarge heap page scans when index and column correlation small.》

《蜂巢的藝術與技術價值 - PostgreSQL PostGIS's hex-grid》

《PostgreSQL 百億地理位置數據 近鄰查詢性能》

https://www.openstreetmap.org/

https://www.postgresql.org/docs/9.6/static/sql-notify.html

https://www.postgresql.org/docs/9.6/static/libpq.html

https://jdbc.postgresql.org/

https://postgis.net/docs/manual-2.3/ST_MakeBox2D.html

最後更新:2017-05-20 01:01:53

  上一篇:go  Windows server 2008 IIS7.5設置https成功了,經驗分享及常見問題解決方法!
  下一篇:go  PostgrSQL 遞歸SQL的幾個應用 - 極客與正常人的思維