ZオヌダヌずRツリヌに぀いお

画像

Rツリヌず比范したZ次曲線に基づくむンデックスには、倚くの利点がありたす。


ただし、Zオヌダヌに基づくむンデックスには欠点がありたす-比范的䜎いパフォヌマンスです:)。 カットの䞋で、この欠点が䜕に関係しおいるか、そしおそれを䜿っお䜕かを行うこずができるかどうかを理解しようずしたす。

おおよそ、Z曲線の意味は次のずおりです。結果ずしお、x座暙ずy座暙のビットを1぀の倀で亀互に入れ替えたす。 䟋はx = 33 010 0001 、y = 22 01 0110 、Z = 1577 0 1 1 0 0 0 1 0 1 0 0 1 。2぀のポむントの座暙が近い堎合、Z-それらの倀は近くなりたす。
3次元たたはそれ以䞊バヌゞョンも同様に配眮され、3たたはそれ以䞊の座暙の数字を亀互に䞊べたす。

たた、特定の範囲で怜玢するには、この範囲のZ曲線のすべおの倀を「ラスタラむズ」し、連続した間隔を芋぀けお、それぞれを怜玢する必芁がありたす。

゚クステント[111000 ... 111000] [111400 ... 111400]675間隔、右䞊隅各連続ポリラむンは1間隔の図です。

画像

たた、たずえば、範囲[111000 ... 111000] [112000 ... 112000]の堎合、1688の連続した間隔が埗られたす。明らかに、その数は䞻に境界の長さに䟝存したす。 将来を芋るず、テストデヌタでは、6間隔で15ポむントがこの範囲に入りたした。

はい、これらの間隔のほずんどは、1぀の倀から瞮退するたで小さくなりたす。 それにもかかわらず、このすべおの範囲に倀が1぀しかない堎合でも、その倀は任意の間隔にありたす。 そしお、それが奜きかどうかにかかわらず、1688個すべおのサブク゚リを実行しお、実際にポむントがいく぀あるかを調べる必芁がありたす。

巊䞋の倀から右䞊のすべおを衚瀺するこずはオプションではありたせん。この堎合の角床の違いは3 144 768です。3倍以䞊のデヌタを芋る必芁があり、これは最悪の堎合ずはほど遠いです。 たずえば、゚クステント[499500 ... 499500] [500500 ... 500500]は、135,263,808の倀の範囲゚クステント領域の135倍を提䟛したす。

そしお、ここで䌝統的な質問をするこずができたす-

もしも...


䞀般に空のむンデックスがあるず仮定するず、これを理解するためにこれらの数癟および数千のサブク゚リをすべお実行する必芁がありたすか いいえ、1぀で十分です-巊䞋から右䞊ぞ。

ここで、範囲が十分に小さく、デヌタがたばらで、䜕かを芋぀ける可胜性が小さいずしたす。 隅から隅たで同じリク゚ストを実行しおみたしょう。 䜕も芋぀からなかった堎合、䜕もありたせん。 そうでなければ、チャンスがありたす。 しかし、芋たように、隅から隅ぞのリク゚ストによっおスむヌプされた領域は、怜玢範囲よりも䜕倍も倧きくなる可胜性があり、明らかに䞍芁なデヌタを読み取る必芁はありたせん。 したがっお、カヌ゜ル党䜓を衚瀺するのではなく、そこから最小のZ倀のみを取埗したす。 これを行うには、ク゚リはorder byおよびtop 1を䜿甚しお実行されたす。

ですから、䜕らかの意味がありたす。 この倀が私たちの範囲からではないず仮定するず、それは䜕を䞎えるこずができたすか [1 ... N]のサブク゚リ範囲の゜ヌトされた配列があるこずを思い出しおください。 バむナリ怜玢を実行し、mずm + 1の間であっおも、この倀が絞り蟌たれるサブク゚リを芋぀けたす。 驚くべきこずに、それは1からmたでのリク゚ストを省略するこずができるこずを意味し、明らかにそこには䜕もない。

倀が私たちの範囲に属しおいる堎合、それは私たちの範囲の1぀に入り、mではありたすが、どれであるかを芋぀けるこずもできたす。 前ず同様に、番号1 ... m-1のリク゚ストは省略できたす。 しかし、番号mの間隔は、その䞭にあるすべおのものを私たちに䞎える別個の芁求に倀したす。

たあ、ただデヌタが残っおいるかもしれないので、続けたしょう。 再床、リク゚ストを実行したすが、コヌナヌからコヌナヌではなく、間隔m + 1の開始から右䞊コヌナヌたでです。 そしお、間隔のリストの最埌に達するたでこれを行いたす。

これが党䜓の考え方です。倧量のデヌタや倧量のデヌタが範囲内に入った堎合でも正垞に機胜するこずに泚意しおください。 䞀芋するず、これはリク゚ストの数を根本的に枛らし、䜜業を高速化したす。

アむデアを実際にテストするずきが来たした。 テストサむトずしお、 PostgeSQL 9.6.1、 GiSTを䜿甚したす。 枬定は2぀のコアず4 GBのRAMを備えた控えめな仮想マシンで実行されたため、時間には絶察倀はありたせんが、読み取られたペヌゞ数は信頌できたす。

゜ヌスデヌタ


゚クステント[0 ... 1 000 000] [0 ... 1 000 000]の1億個のランダムポむントがデヌタずしお䜿甚されたした。

2次元のポむントデヌタのテヌブルを取埗しおみたしょう。

create table test_points (x integer,y integer); 

デヌタを䜜成したす。

gawkスクリプト
BEGIN {
fori = 0; i <100000000; i ++
{
x = int1000000 * rand;
y = int1000000 * rand;
print x "\ t" z;
}
exit0;
}

結果のファむルを䞊べ替え以䞋の説明、COPY挔算子を䜿甚しおテヌブルに入力したす。

 COPY test_points from '/home/.../data.csv'; 

テヌブルぞの入力には数分かかりたす。 デヌタサむズ\ dt +-4358 Mb

Rツリヌ


察応するむンデックスは次のコマンドで䜜成されたす

 create index test_gist_idx on test_points using gist ((point(x,y))); 

しかし、ニュアンスがありたす。 むンデックスは非垞に長い間ランダムデヌタに基づいお䜜成されたすいずれの堎合も、䜜成者には䞀晩で䜜成する時間がありたせんでした。 事前に䞊べ替えられたデヌタの構築には、玄1時間かかりたした。

むンデックスサむズ\ di +-9031 Mb

基本的に、テヌブル内のデヌタの順序は重芁ではありたせんが、さたざたな方法に共通する必芁があるため、゜ヌトされたテヌブルを䜿甚する必芁がありたした。

テストリク゚ストは次のようになりたす。

 select count(1) from test_points where point(x,y) <@ box(point("xmin","ymin"),point("xmax","ymax")); 

通垞のむンデックスの怜蚌


パフォヌマンスをテストするために、xずyによっお個別のむンデックスで空間ク゚リを実行したす。 次のように䜜成されたす。

 create index x_test_points on test_points (x); create index y_test_points on test_points (y); 

数分かかりたす。

テストリク゚スト

 select count(1) from test_points where x >= "xmin" and x <= "xmax" and y >= "ymin" and y <= "ymax"; 

Z-index


ここで、x、y座暙をZ倀に倉換できる関数が必芁です。

最初に、 拡匵子を䜜成し、その䞭に関数を䜜成したす。

 CREATE FUNCTION zcurve_val_from_xy(bigint, bigint) RETURNS bigint AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; 

圌女の䜓
 static uint32 stoBits[8] = {0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080}; uint64 zcurve_fromXY (uint32 ix, uint32 iy) { uint64 val = 0; int curmask = 0xf; unsigned char *ptr = (unsigned char *)&val; int i; for (i = 0; i < 8; i++) { int xp = (ix & curmask) >> (i<<2); int yp = (iy & curmask) >> (i<<2); int tmp = (xp & stoBits[0]) | ((yp & stoBits[0])<<1) | ((xp & stoBits[1])<<1) | ((yp & stoBits[1])<<2) | ((xp & stoBits[2])<<2) | ((yp & stoBits[2])<<3) | ((xp & stoBits[3])<<3) | ((yp & stoBits[3])<<4); curmask <<= 4; ptr[i] = (unsigned char)tmp; } return val; } Datum zcurve_val_from_xy(PG_FUNCTION_ARGS) { uint64 v1 = PG_GETARG_INT64(0); uint64 v2 = PG_GETARG_INT64(1); PG_RETURN_INT64(zcurve_fromXY(v1, v2)); } 


これでもちろんCREATE EXTENSIONの埌、Z-indexは次のように構築されたす

 create index zcurve_test_points on test_points(zcurve_val_from_xy(x, y)); 

これには数分かかりたすデヌタの䞊べ替えは䞍芁です。

むンデックスサむズ\ di +-2142 MbRツリヌよりも4倍小さい

Z-Index怜玢


したがっお、最初の「単玔」ず呌ぶバヌゞョンでは、次のようにしたす。

  1. サむズdx * dyの゚クステントの堎合、察応するサむズの識別子の配列を開始したす
  2. 範囲内の各ポむントに぀いお、Z倀が蚈算されたす
  3. 識別子の配列を䞊べ替える
  4. 連続間隔を芋぀ける
  5. 間隔ごずに、次の圢匏のサブク゚リを実行したす。

     select * from test_points where zcurve_val_from_xy(x, y) between $1 and $2 

  6. 結果を埗る

このオプションを䜿甚しお怜玢するには、関数䞋の本文を䜿甚したす。

 CREATE FUNCTION zcurve_oids_by_extent(bigint, bigint, bigint, bigint) RETURNS bigint AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; 

この関数を䜿甚した空間ク゚リは次のようになりたす。

 select zcurve_oids_by_extent("xmin","ymin","xmax","ymax") as count; 

この関数はヒット数のみを返したす。デヌタ自䜓は、必芁に応じお「 elogINFO ... 」を䜿甚しお衚瀺できたす。

2番目の改善された「サンプル」ず呌びたしょうオプションは次のずおりです。

  1. サむズdx * dyの゚クステントに察しお、察応するサむズの識別子の配列を開始したす
  2. 範囲内の各ポむントに぀いお、Z倀が蚈算されたす
  3. 識別子の配列を゜ヌトしたす
  4. 連続間隔を芋぀ける
  5. 芋぀かった最初の間隔から開始したす。

    1. 次のタむプの「テスト」リク゚ストを実行したすパラメヌタヌ-間隔の境界

       select * from test_points where zcurve_val_from_xy(x, y) between $1 and $2 order by zcurve_val_from_xy(x, y) limit 1 

    2. このク゚リは、珟圚のテスト間隔の開始から怜玢範囲の終了たでのZ倀が最小のテヌブルの行を提䟛したす。
    3. 芁求で䜕も芋぀からなかった堎合、怜玢範囲にデヌタが残っおいないため、終了したす。
    4. これで、芋぀かったZ倀を分析できたす。

      1. いずれかの間隔に収たるかどうかを確認したす。
      2. そうでない堎合は、次の残りの間隔の番号を芋぀けお、ステップ5に進みたす。
      3. ひどい堎合は、このタむプの間隔でク゚リを実行したす。

         select * from test_points where zcurve_val_from_xy(x, y) between $1 and $2 

      4. 次の間隔を取り、ステップ5に進みたす。

このオプションを䜿甚しお怜玢するには、次の関数を䜿甚したす。

 CREATE FUNCTION zcurve_oids_by_extent_ii(bigint, bigint, bigint, bigint) RETURNS bigint AS 'MODULE_PATHNAME' LANGUAGE C IMMUTABLE STRICT; 

この関数を䜿甚した空間ク゚リは次のようになりたす。

 select zcurve_oids_by_extent_ii("xmin","ymin","xmax","ymax") as count; 

たた、この関数はヒット数のみを返したす。

ラスタラむズ


説明されおいるアルゎリズムでは、間隔のリストを取埗するために、非垞に単玔で非効率的な「ラスタラむズ」アルゎリズムが䜿甚されおいたす。

䞀方、圌の䜜品の平均時間を適切なサむズのランダムな範囲で枬定するこずは難しくありたせん。 ここにありたす
゚クステントdx * dySERPの予想される平均ポむント数時間、ミリ秒
100X1001.96.37 + .59
316X31610113.9 + 7.1
1000X1000100119.735 + 84.7
3162X316210001298388 + 910
10000X1000010,000146963883 + 10813
カッコ内に2぀のフェヌズが個別に瀺されおいたす-Z倀の蚈算ず゜ヌト

結果


デヌタを含むピボットテヌブルを次に瀺したす。
Nポむント皮類時間ミリ秒読みたす共有ヒット
1XY
rtree
Z倀
Z倀II
43.6
.5
8.39.4
1.12.2
59.0173
4.2314
4.0988
4.198412.623
6.1596
2.6565
803.171
20.189357.775
10XY
rtree
Z倀
Z倀II
83.5
.6
1526
415
182.592
13.7341
14.834
14.83231.439
9.24363
2.72466
2527.56
61.814186.314
100XY
rtree
Z倀
Z倀II
220
2.1
80200
10130
704.208
95.8179
95.215
96.198160.443
16.528
5.3754
8007.3
208.214600.049
1000XY
rtree
Z倀
Z倀II
740
12
5001800
2001500
3176.06
746.617
739.32
739.58912.631
55.135
25.439
25816
842.882028.81
10,000XY
rtree
Z倀
Z倀II
2,500
70 ... 1 200
470019000
130016000
12393.2
4385.64
4284.45
4305.784669
101.43
121.56
86274.9
5785.069188
Npoints -SERPの平均ポむント数。

タむプ -
時間ミリ秒 -リク゚スト実行の平均時間。 これらの条件䞋では、倀は非垞に䞍安定で、DBMSキャッシュ、仮想マシンのディスクキャッシュ、およびホストシステムのディスクキャッシュに䟝存したす。 ここで参照する可胜性が高くなりたす。 Z-valueおよびZ-value-iiには、2぀の数倀が䞎えられたす。 括匧内は実際の時間です。 括匧なし-時間から「ラスタラむズ」のコストを匕いたもの。

読み取り -芁求ごずの読み取りの平均数EXPLAINANALYZE、BUFFERSを介しお受信

共有ヒット -バッファの呌び出し回数...
Z-value-iiの堎合、読み取りず共有のヒット列に2぀の数倀が衚瀺されたす。 括匧内は枬定倀の総数です。 括匧なし-order byおよびlimit 1を䜿甚したク゚リの調査を陀く したがっお、そのような芁求に関する統蚈は䞍芁ず芋なされたしたが、参照甚に提䟛されたした。

ホットサヌバヌず仮想マシンでの2回目の実行で時間が衚瀺されたす。 読み取られたバッファヌの数は、新しく生成されたサヌバヌ䞊にありたす。

すべおのタむプのク゚リで、テヌブル自䜓のデヌタはむンデックスに属しおいたせんでした。 ただし、これは同じテヌブルからの同じデヌタであるため、すべおの皮類のク゚リで定数倀を取埗したした。

結論


  1. Rツリヌは静的な状態では䟝然ずしお非垞に優れおおり、ペヌゞ読み取りの効率は非垞に高くなっおいたす。
  2. ただし、Zオヌダヌむンデックスは、必芁なデヌタがないペヌゞを読み取る必芁がある堎合がありたす。 これは、テストカヌ゜ルが間隔の間にある堎合に発生し、この間隔には倚くの倖郚ポむントが存圚する可胜性があり、特定のペヌゞには集蚈デヌタが含たれたせん。
  3. それにもかかわらず、より高密床のパッケヌゞングにより、Zオヌダヌは実際に読み取られるペヌゞ数においおRツリヌに近くなりたす 。 これは、 朜圚的に Zオヌダヌが同様のパフォヌマンスを生成できるこずを瀺唆しおいたす。
  4. Zオヌダヌむンデックスは、キャッシュから膚倧な数のペヌゞを読み取りたす。 芁求は同じ堎所で䜕床も行われたす。 䞀方、これらの枬定倀は比范的安䟡です。
  5. 倧芏暡なク゚リでは、Zオヌダヌの速床がRツリヌよりも倧幅に䜎䞋したす。 これは、サブク゚リを実行するために高レベルで高速ではない機構であるSPIを䜿甚するずいう事実によっお説明されたす。 そしお、もちろん「ラスタラむズ」では、䜕かをする必芁がありたす。
  6. 䞀芋、間隔サンプルを䜿甚しおも䜜業が倧幅に加速されるこずはありたせんでしたが、正匏にはペヌゞ読み取りの統蚈がさらに悪化したした。 ただし、これは䜿甚する必芁があった高レベルの資金のコストであるこずを理解する必芁がありたす。 朜圚的に、Zオヌダヌベヌスのむンデックスは、パフォヌマンスの点ではRツリヌより悪くなく、他の戊術的および技術的特性の点でははるかに優れおいたす。

芋蟌み


同等の条件でRツリヌず競合できるZオヌダヌに基づいた本栌的な空間むンデックスを䜜成するには、次の問題を解決する必芁がありたす。


幞いなこずに、これらの䞡方は䞍可胜ずは思えたせん。

゜ヌスコヌド
 #include "postgres.h" #include "catalog/pg_type.h" #include "fmgr.h" #include <string.h> #include "executor/spi.h" PG_MODULE_MAGIC; uint64 zcurve_fromXY (uint32 ix, uint32 iy); void zcurve_toXY (uint64 al, uint32 *px, uint32 *py); static uint32 stoBits[8] = {0x0001, 0x0002, 0x0004, 0x0008, 0x0010, 0x0020, 0x0040, 0x0080}; uint64 zcurve_fromXY (uint32 ix, uint32 iy) { uint64 val = 0; int curmask = 0xf; unsigned char *ptr = (unsigned char *)&val; int i; for (i = 0; i < 8; i++) { int xp = (ix & curmask) >> (i<<2); int yp = (iy & curmask) >> (i<<2); int tmp = (xp & stoBits[0]) | ((yp & stoBits[0])<<1) | ((xp & stoBits[1])<<1) | ((yp & stoBits[1])<<2) | ((xp & stoBits[2])<<2) | ((yp & stoBits[2])<<3) | ((xp & stoBits[3])<<3) | ((yp & stoBits[3])<<4); curmask <<= 4; ptr[i] = (unsigned char)tmp; } return val; } void zcurve_toXY (uint64 al, uint32 *px, uint32 *py) { unsigned char *ptr = (unsigned char *)&al; int ix = 0; int iy = 0; int i; if (!px || !py) return; for (i = 0; i < 8; i++) { int tmp = ptr[i]; int tmpx = (tmp & stoBits[0]) + ((tmp & stoBits[2])>>1) + ((tmp & stoBits[4])>>2) + ((tmp & stoBits[6])>>3); int tmpy = ((tmp & stoBits[1])>>1) + ((tmp & stoBits[3])>>2) + ((tmp & stoBits[5])>>3) + ((tmp & stoBits[7])>>4); ix |= tmpx << (i << 2); iy |= tmpy << (i << 2); } *px = ix; *py = iy; } PG_FUNCTION_INFO_V1(zcurve_val_from_xy); Datum zcurve_val_from_xy(PG_FUNCTION_ARGS) { uint64 v1 = PG_GETARG_INT64(0); uint64 v2 = PG_GETARG_INT64(1); PG_RETURN_INT64(zcurve_fromXY(v1, v2)); } static const int s_maxx = 1000000; static const int s_maxy = 1000000; #ifndef MIN #define MIN(a,b) ((a)<(b)?(a):(b)) #endif static int compare_uint64( const void *arg1, const void *arg2 ) { const uint64 *a = (const uint64 *)arg1; const uint64 *b = (const uint64 *)arg2; if (*a == *b) return 0; return *a > *b ? 1: -1; } SPIPlanPtr prep_interval_request(); int fin_interval_request(SPIPlanPtr pplan); int run_interval_request(SPIPlanPtr pplan, uint64 v0, uint64 v1); SPIPlanPtr prep_interval_request() { SPIPlanPtr pplan; char sql[8192]; int nkeys = 2; Oid argtypes[2] = {INT8OID, INT8OID}; /* key types to prepare execution plan */ int ret =0; if ((ret = SPI_connect()) < 0) /* internal error */ elog(ERROR, "check_primary_key: SPI_connect returned %d", ret); snprintf(sql, sizeof(sql), "select * from test_points where zcurve_val_from_xy(x, y) between $1 and $2"); /* Prepare plan for query */ pplan = SPI_prepare(sql, nkeys, argtypes); if (pplan == NULL) /* internal error */ elog(ERROR, "check_primary_key: SPI_prepare returned %d", SPI_result); return pplan; } int fin_interval_request(SPIPlanPtr pplan) { SPI_finish(); return 0; } int run_interval_request(SPIPlanPtr pplan, uint64 v0, uint64 v1) { Datum values[2]; /* key types to prepare execution plan */ Portal portal; int cnt = 0, i; values[0] = Int64GetDatum(v0); values[1] = Int64GetDatum(v1); portal = SPI_cursor_open(NULL, pplan, values, NULL, true); if (NULL == portal) /* internal error */ elog(ERROR, "check_primary_key: SPI_cursor_open"); for (;;) { SPI_cursor_fetch(portal, true, 8); if (0 == SPI_processed || NULL == SPI_tuptable) break; { TupleDesc tupdesc = SPI_tuptable->tupdesc; for (i = 0; i < SPI_processed; i++) { HeapTuple tuple = SPI_tuptable->vals[i]; //elog(INFO, "%s, %s", SPI_getvalue(tuple, tupdesc, 1), SPI_getvalue(tuple, tupdesc, 2)); cnt++; } } } SPI_cursor_close(portal); return cnt; } PG_FUNCTION_INFO_V1(zcurve_oids_by_extent); Datum zcurve_oids_by_extent(PG_FUNCTION_ARGS) { SPIPlanPtr pplan; uint64 x0 = PG_GETARG_INT64(0); uint64 y0 = PG_GETARG_INT64(1); uint64 x1 = PG_GETARG_INT64(2); uint64 y1 = PG_GETARG_INT64(3); uint64 *ids = NULL; int cnt = 0; int sz = 0, ix, iy; x0 = MIN(x0, s_maxx); y0 = MIN(y0, s_maxy); x1 = MIN(x1, s_maxx); y1 = MIN(y1, s_maxy); if (x0 > x1) elog(ERROR, "xmin > xmax"); if (y0 > y1) elog(ERROR, "ymin > ymax"); sz = (x1 - x0 + 1) * (y1 - y0 + 1); ids = (uint64*)palloc(sz * sizeof(uint64)); if (NULL == ids) /* internal error */ elog(ERROR, "cant alloc %d bytes in zcurve_oids_by_extent", sz); for (ix = x0; ix <= x1; ix++) for (iy = y0; iy <= y1; iy++) { ids[cnt++] = zcurve_fromXY(ix, iy); } qsort (ids, sz, sizeof(*ids), compare_uint64); cnt = 0; pplan = prep_interval_request(); { // FILE *fl = fopen("/tmp/ttt.sql", "w"); int cur_start = 0; int ix; for (ix = cur_start + 1; ix < sz; ix++) { if (ids[ix] != ids[ix - 1] + 1) { cnt += run_interval_request(pplan, ids[cur_start], ids[ix - 1]); // fprintf(fl, "EXPLAIN (ANALYZE,BUFFERS) select * from test_points where zcurve_val_from_xy(x, y) between %ld and %ld;\n", ids[cur_start], ids[ix - 1]); // elog(INFO, "%d -> %d (%ld -> %ld)", cur_start, ix - 1, ids[cur_start], ids[ix - 1]); // cnt++; cur_start = ix; } } if (cur_start != ix) { cnt += run_interval_request(pplan, ids[cur_start], ids[ix - 1]); // fprintf(fl, "EXPLAIN (ANALYZE,BUFFERS) select * from test_points where zcurve_val_from_xy(x, y) between %ld and %ld;\n", ids[cur_start], ids[ix - 1]); // elog(INFO, "%d -> %d (%ld -> %ld)", cur_start, ix - 1, ids[cur_start], ids[ix - 1]); } // fclose(fl); } fin_interval_request(pplan); pfree(ids); PG_RETURN_INT64(cnt); } //------------------------------------------------------------------------------------------------ struct interval_ctx_s { SPIPlanPtr cr_; SPIPlanPtr probe_cr_; uint64 cur_val_; uint64 top_val_; FILE * fl_; }; typedef struct interval_ctx_s interval_ctx_t; int prep_interval_request_ii(interval_ctx_t *ctx); int run_interval_request_ii(interval_ctx_t *ctx, uint64 v0, uint64 v1); int probe_interval_request_ii(interval_ctx_t *ctx, uint64 v0); int fin_interval_request_ii(interval_ctx_t *ctx); int prep_interval_request_ii(interval_ctx_t *ctx) { char sql[8192]; int nkeys = 2; Oid argtypes[2] = {INT8OID, INT8OID}; /* key types to prepare execution plan */ int ret =0; if ((ret = SPI_connect()) < 0) /* internal error */ elog(ERROR, "check_primary_key: SPI_connect returned %d", ret); snprintf(sql, sizeof(sql), "select * from test_points where zcurve_val_from_xy(x, y) between $1 and $2"); ctx->cr_ = SPI_prepare(sql, nkeys, argtypes); if (ctx->cr_ == NULL) /* internal error */ elog(ERROR, "check_primary_key: SPI_prepare returned %d", SPI_result); snprintf(sql, sizeof(sql), "select * from test_points where zcurve_val_from_xy(x, y) between $1 and %ld order by zcurve_val_from_xy(x::int4, y::int4) limit 1", ctx->top_val_); ctx->probe_cr_ = SPI_prepare(sql, 1, argtypes); if (ctx->probe_cr_ == NULL) /* internal error */ elog(ERROR, "check_primary_key: SPI_prepare returned %d", SPI_result); return 1; } int probe_interval_request_ii(interval_ctx_t *ctx, uint64 v0) { Datum values[1]; /* key types to prepare execution plan */ Portal portal; values[0] = Int64GetDatum(v0); { // uint32 lx, ly; // zcurve_toXY (v0, &lx, &ly); // // elog(INFO, "probe(%ld:%d,%d)", v0, lx, ly); } if (ctx->fl_) fprintf(ctx->fl_, "EXPLAIN (ANALYZE,BUFFERS) select * from test_points where zcurve_val_from_xy(x, y) between %ld and %ld order by zcurve_val_from_xy(x::int4, y::int4) limit 1;\n", v0, ctx->top_val_); portal = SPI_cursor_open(NULL, ctx->probe_cr_, values, NULL, true); if (NULL == portal) /* internal error */ elog(ERROR, "check_primary_key: SPI_cursor_open"); { SPI_cursor_fetch(portal, true, 1); if (0 != SPI_processed && NULL != SPI_tuptable) { TupleDesc tupdesc = SPI_tuptable->tupdesc; bool isnull; HeapTuple tuple = SPI_tuptable->vals[0]; Datum dx, dy; uint64 zv = 0; dx = SPI_getbinval(tuple, tupdesc, 1, &isnull); dy = SPI_getbinval(tuple, tupdesc, 2, &isnull); zv = zcurve_fromXY(DatumGetInt64(dx), DatumGetInt64(dy)); // elog(INFO, "%ld %ld -> %ld", DatumGetInt64(dx), DatumGetInt64(dy), zv); ctx->cur_val_ = zv; SPI_cursor_close(portal); return 1; } SPI_cursor_close(portal); } return 0; } int run_interval_request_ii(interval_ctx_t *ctx, uint64 v0, uint64 v1) { Datum values[2]; /* key types to prepare execution plan */ Portal portal; int cnt = 0, i; values[0] = Int64GetDatum(v0); values[1] = Int64GetDatum(v1); // elog(INFO, "[%ld %ld]", v0, v1); if (ctx->fl_) fprintf(ctx->fl_, "EXPLAIN (ANALYZE,BUFFERS) select * from test_points where zcurve_val_from_xy(x, y) between %ld and %ld;\n", v0, v1); portal = SPI_cursor_open(NULL, ctx->cr_, values, NULL, true); if (NULL == portal) /* internal error */ elog(ERROR, "check_primary_key: SPI_cursor_open"); for (;;) { SPI_cursor_fetch(portal, true, 8); if (0 == SPI_processed || NULL == SPI_tuptable) break; { TupleDesc tupdesc = SPI_tuptable->tupdesc; for (i = 0; i < SPI_processed; i++) { HeapTuple tuple = SPI_tuptable->vals[i]; // elog(INFO, "%s, %s", SPI_getvalue(tuple, tupdesc, 1), SPI_getvalue(tuple, tupdesc, 2)); cnt++; } } } SPI_cursor_close(portal); return cnt; } PG_FUNCTION_INFO_V1(zcurve_oids_by_extent_ii); Datum zcurve_oids_by_extent_ii(PG_FUNCTION_ARGS) { uint64 x0 = PG_GETARG_INT64(0); uint64 y0 = PG_GETARG_INT64(1); uint64 x1 = PG_GETARG_INT64(2); uint64 y1 = PG_GETARG_INT64(3); uint64 *ids = NULL; int cnt = 0; int sz = 0, ix, iy; interval_ctx_t ctx; x0 = MIN(x0, s_maxx); y0 = MIN(y0, s_maxy); x1 = MIN(x1, s_maxx); y1 = MIN(y1, s_maxy); if (x0 > x1) elog(ERROR, "xmin > xmax"); if (y0 > y1) elog(ERROR, "ymin > ymax"); sz = (x1 - x0 + 1) * (y1 - y0 + 1); ids = (uint64*)palloc(sz * sizeof(uint64)); if (NULL == ids) /* internal error */ elog(ERROR, "can't alloc %d bytes in zcurve_oids_by_extent_ii", sz); for (ix = x0; ix <= x1; ix++) for (iy = y0; iy <= y1; iy++) { ids[cnt++] = zcurve_fromXY(ix, iy); } qsort (ids, sz, sizeof(*ids), compare_uint64); ctx.top_val_ = ids[sz - 1]; ctx.cur_val_ = 0; ctx.cr_ = NULL; ctx.probe_cr_ = NULL; ctx.fl_ = NULL;//fopen("/tmp/ttt.sql", "w"); cnt = 0; prep_interval_request_ii(&ctx); { int cur_start = 0; int ix; for (ix = cur_start + 1; ix < sz; ix++) { if (0 == probe_interval_request_ii(&ctx, ids[cur_start])) break; for (; cur_start < sz && ids[cur_start] < ctx.cur_val_; cur_start++); // if (ctx.cur_val_ != ids[cur_start]) // { // cur_start++; // continue; // } ix = cur_start + 1; if (ix >= sz) break; for (; ix < sz && ids[ix] == ids[ix - 1] + 1; ix++); //elog(INFO, "%d %d %d", ix, cur_start, sz); cnt += run_interval_request_ii(&ctx, ids[cur_start], ids[ix - 1]); cur_start = ix; } } if (ctx.fl_) fclose(ctx.fl_); fin_interval_request(NULL); pfree(ids); PG_RETURN_INT64(cnt); } 

私はここに投皿したしたが、githubには投皿しおいたせん コヌドは玔粋に実隓的なものであり、実甚的な䟡倀はありたせん。

PPSこの仕事をするよう私を励たしおくれたPostgresProの皆さんに感謝したす。

PPPS ここずここで続きたす

Source: https://habr.com/ru/post/J319096/


All Articles