1回の選択でマップを描画するか、マルチテーブルインデックスの利点について描画します

画像

この記事は、シンプルで生産的な動的Webマップサーバーのプロトタイピングを説明するシリーズの続きで書かれました。 前に 、空間インデックスの配置方法と、空間レイヤーを取得および描画する方法について説明しました 。 これからもう少しエレガントにしましょう。



1つのレイヤーの操作に関しては、多かれ少なかれわかりましたが、実際には何十ものレイヤーが存在する可能性があり、検索範囲に何も入らなくても、それぞれが空間インデックスで要求される必要があります...このプロセスをアーキテクチャ上きれいにし、明らかに役に立たないアクションを取り除くには?



索引付け



可能です、そうではありません。 ただし、このためには、すべての空間レイヤーに対して単一の空間インデックスを作成する必要があります。 インデックスの構造を思い出してください:



単一のインデックスを作成するには、すべてのレイヤーを1つのグリッドで分割し、同じ方法でブロックに番号を付け、オブジェクト識別子に加えて、テーブル識別子もインデックスに保存する必要があります。 だから:



インデックスのパフォーマンスを測定します。



一般的なインデックスの古い[35 ... 45.50 ... 60]°の正方形で、10,000回のランダム検索を実行します。 すべてのリクエストは1つの接続で実行されます。 シングルコアでパフォーマンスを表示します。

リクエストサイズ 単一インデックスの時間
0.01° 7'35 '' 8'26 ''
0.1° 8'25 '' 9'20 ''
0.5° 15'11 '' 16'7 ''
結論 :はい、単一のインデックスはより速く動作しますが、結果は驚くほどではありません。 一方、4つのレイヤーすべてが比較的密集しているため、1回の検索で得られるすべての利点を十分に活用できるように、データ量が比較的少ないことが重要です。 第三に、これはそのままのライブデータです。



地図を描く



まず最初に、 古い方法でそれを描画しますので、比較するものがあります。

 create procedure mk_test_gif( in cminx double precision, in cminy double precision, in cmaxx double precision, in cmaxy double precision) { declare img any; img := img_create (512, 512, cminx, cminy, cmaxx, cmaxy, 1); declare cl integer; declare bg integer; { cl := img_alloc_color (img, 0, 0, 255); bg := img_alloc_color (img, 0, 0, 255); whenever not found goto nf; for select blob_to_string(Shape) as data from xxx.YYY."water-polygon" as x, xxx.YYY."v_water-polygon_spx_enum_items_in_box" as a where a.minx = cminx and a.miny = cminy and a.maxx = cmaxx and a.maxy = cmaxy and x."_OBJECTID_" = a.oid and x.maxx_ >= cminx and x.minx_ <= cmaxx and x.maxy_ >= cminy and x.miny_ <= cmaxy do { img_draw_polygone (img, data, cl, bg); } nf:; ...      ,    img_draw_polyline. ... } declare ptr integer; ptr := img_tostr (img); img_destroy (img); declare image any; image := img_fromptr(ptr); string_to_file('test.gif', image, -2); return; }; mk_test_gif(35., 50., 45., 60.);
      
      



作業には17.2秒かかります。



さて、約束どおり、1回の選択で。 頭に浮かぶ最初のもの

集合体

集計は、カーソルが開かれたときに作成され、結果の各行で呼び出され、カーソルが閉じられたときにファイナライズされるオブジェクトです。

 create function mapx_agg_init (inout _agg any) {;}; create function mapx_agg_acc ( inout _agg any, in _tab integer, in _oid integer ) { if (_agg is null) { declare img any; img := img_create (512, 512, _tab[0], _tab[1], _tab[2], _tab[3], 1); _agg := img; return 0; } else { return case when _tab = 4 then (img_draw_polygone(_agg, ( select blob_to_string(bl.Shape) from "xxx"."YYY"."building-polygon" as bl where bl."_OBJECTID_" = _oid), 255, 255)) when _tab = 3 then (img_draw_polyline(_agg, ( select blob_to_string(hw.Shape) from "xxx"."YYY"."highway-line" as hw where hw."_OBJECTID_" = _oid), 100, 100)) when _tab = 2 then (img_draw_polygone(_agg, ( select blob_to_string(vg.Shape) from "xxx"."YYY"."vegetation-polygon" as vg where vg."_OBJECTID_" = _oid), 10, 10)) when _tab = 1 then (img_draw_polygone(_agg, ( select blob_to_string(wt.Shape) from "xxx"."YYY"."water-polygon" as wt where wt."_OBJECTID_" = _oid), 50, 50)) else 1 end; } }; create function mapx_agg_final (inout _agg any) returns integer { declare ptr integer; ptr := img_tostr (_agg); img_destroy (_agg); declare image any; image := img_fromptr(ptr); string_to_file('nskx_ii.gif', image, -2); return 1; }; create aggregate mapx_agg (in _tab integer, in _oid integer) returns integer from mapx_agg_init, mapx_agg_acc, mapx_agg_final; create procedure mk_testx_ii_gif( in cminx double precision, in cminy double precision, in cmaxx double precision, in cmaxy double precision) { declare cnt integer; select mapx_agg(tab, oid) into cnt from ( select * from (select vector(cminx, cminy, cmaxx, cmaxy) as tab, 0 as oid) as f1 union all (select tab, oid from xxx.YYY."v_total__spx_enum_items_in_box" as a where a.minx = cminx and a.miny = cminy and a.maxx = cmaxx and a.maxy = cmaxy) ) f_all; } mk_testx_ii_gif(35., 50., 45., 60.);
      
      



残念ながら、初期化パラメーターを集約に渡すための通常の方法はありません。そのため、データと初期化文字列から結合を取り除いて、コンストラクターではなく、最初の行を取得するときに、トリックに移動する必要があります。

どうしてそれができるのか、注意深い読者は、1つの選択が約束されたと言うでしょう、そして、それらの全部があります! 実際、行識別子は集計に整然と表形式で送られるため、見かけのサブクエリは実際には結合ハンドによって編成されます。

したがって、実行時間は42秒です。 Nda。



別の試み

 create procedure mk_testx_gif( in cminx double precision, in cminy double precision, in cmaxx double precision, in cmaxy double precision) { declare img any; img := img_create (512, 512, cminx, cminy, cmaxx, cmaxy, 1); declare cnt, cnt2 integer; declare cl1, bg1 integer; cl1 := img_alloc_color (img, 0, 0, 255); bg1 := img_alloc_color (img, 0, 0, 255); declare cl2, bg2 integer; cl2 := img_alloc_color (img, 0, 255, 0); bg2 := img_alloc_color (img, 0, 255, 0); declare cl3, bg3 integer; cl3 := img_alloc_color (img, 255, 100, 0); bg3 := img_alloc_color (img, 255, 100, 0); declare cl4, bg4 integer; cl4 := img_alloc_color (img, 255, 0, 0); bg4 := img_alloc_color (img, 255, 0, 0); select sum ( case when geom is null then 0 when geom_type = 2 then (img_draw_polyline(img, geom, cl, bg)) else (img_draw_polygone(img, geom, cl, bg)) end ) into cnt from ( select case when a.tab = 4 then ( select blob_to_string(bl.Shape) from "xxx"."YYY"."building-polygon" as bl where bl."_OBJECTID_" = a.oid) when a.tab = 3 then ( select blob_to_string(hw.Shape) from "xxx"."YYY"."highway-line" as hw where hw."_OBJECTID_" = a.oid) when a.tab = 2 then ( select blob_to_string(vg.Shape) from "xxx"."YYY"."vegetation-polygon" as vg where vg."_OBJECTID_" = a.oid) when a.tab = 1 then ( select blob_to_string(wt.Shape) from "xxx"."YYY"."water-polygon" as wt where wt."_OBJECTID_" = a.oid) else '' end as geom, case when a.tab = 3 then 2 else 1 end as geom_type, case when a.tab = 4 then cl4 when a.tab = 3 then cl3 when a.tab = 2 then cl2 when a.tab = 1 then cl1 else 0 end as cl, case when a.tab = 4 then bg4 when a.tab = 3 then bg3 when a.tab = 2 then bg2 when a.tab = 1 then bg1 else 0 end as bg from xxx.YYY."v_total__spx_enum_items_in_box" as a where a.minx = cminx and a.miny = cminy and a.maxx = cmaxx and a.maxy = cmaxy ) f_all; declare ptr integer; ptr := img_tostr (img); img_destroy (img); declare image any; image := img_fromptr(ptr); string_to_file('testx.gif', image, -2); return; }; mk_testx_gif(35., 50., 45., 60.);
      
      



このクエリは17.4秒実行されます。 したがって、オプティマイザーは、隠れた結合を認識し、美しさをあまり失うことなく要求を実行することができました。 インデックス自体の作業のわずかな増加は、リクエストの複雑さの増加によって食い尽くされました。



結果は次のとおりです。

画像

この説明のない画像には数百万のオブジェクトがあります。



結論



1つのリクエストで地図を描くことは難しくありません。 パフォーマンスをあまり失うことなく、これを実行することさえできました。 確かに、それも勝つことに失敗し、その理由はデータ構造にあるようです。 また、この構造は、従来のGISで使用するために強化されています。



たとえば、テーブル「highway-line」には、属性が異なるさまざまなタイプのレイヤーが数十個含まれています。 通常、このようなテーブルは、同じ物理テーブルを参照し、フィルターが異なるすべての道路レイヤーの基礎として機能します。 もちろん、2ダースよりも1つのテーブルで作業する方が便利です(この点もこの作業の動機の1つでした)。 繰り返しますが、これらすべてのレイヤーに共通の空間インデックスがあります。



しかし、欠点もあります。 それでも、各レイヤーを描画するには、個別のSQLクエリを実行する必要があります。 つまり 1つのインデックスがありますが、それでもいくつかの検索があります。 最も勝てるのはページのキャッシュです。 追加のインデックスが必要です-レコードのタイプ。それを検索してサンプルを横断することも必要です。 さらに、異なるタイプのオブジェクトが混在しているため、同じタイプのオブジェクトが並んで(1ページに)表示される可能性が低く、それによって読み取りの総数が増加します。



たとえば、上記のように、「高速道路」テーブルをタイプごとに多数のサブテーブルに分散し、それらすべてを1つの空間インデックスに結合するとどうなりますか? インデックスの操作はこれから変更されることはなく、インデックスを1回検索するだけで済みます。 データの操作は加速するだけです データの局所性が向上します。同じタイプのデータが空間的に近い頻度でディスクに並んで表示されます。 また、検索範囲にデータの種類がない場合、単に処理されません。 どれだけ多くても、有用なデータの読み取りには影響しません。



そして最後の観測。 インデックス自体はかなり奇妙なオブジェクトです。 関係代数にも関係計算にも近いものはありません。 これは、クエリプロセッサがそれらをより効率的に実行できるようにする実装固有の拡張機能です。 私たちの場合、インデックスにはデータにないいくつかのセマンティクスがロードされます。 マルチテーブルインデックスは、レイヤー間の関係を表します。実際、特定のインデックスには、一緒に描画したいレイヤーが含まれます。



一方、インデックスをテーブルとして認識することはできません(実際はテーブルですが、これは特定のDBMS内に留まることを余儀なくされるため、これは必要な手段です)。 その値はテーブル識別子です。 これはメタテーブルであり、クエリプランはこのメタデータに依存します。



繰り返しますが、伝統的に、クエリプロセッサは、使用するインデックスを自由に選択できます。 しかし、これは私たちの場合ではありません。 いくつかのマルチテーブルインデックスを作成し、プライマリデータストリームのソースであるインデックスを明示的に示すことができます。 オプティマイザーがそれについてどう考えているかに関係なく。 関係モデルの「ドライ理論」を通して「生命の木は常に緑」であることがわかります。



PS:

例として、記事の見出しは、同名の物語からのマーク・トウェインの手による素晴らしい「パリの街の計画」を使用しています。



All Articles