関数を使用した効率向上の構築
ユーザー定義関数は、デフォルトでは単一シャードの最適化は行われませんが、シングルシャードオペレーションとして実行するように設定できます。関数はロジックをカプセル化し、単一シャードの最適化方式で実行できます。
単一シャードオペレーションが重要な理由
リソース使用率は、パフォーマンスとコスト効率にとって重要です。単一シャードオペレーションでは、クロスシャードオペレーションに比べてリソースの使用が大幅に少なくなります。例えば、関数を実行して 100 万行を挿入する場合、単一シャード実行では約 90.5 ACU、クロスシャード実行では 126.5 ACU が使用されるため、リソース効率が 35% 向上します。
単一シャード実行では、以下も提供されます。
-
クロスシャードオペレーションよりもスループットが 35% 高い
-
より予測可能な応答時間
-
データの増加に伴うスケーラビリティの向上
シングルシャードオペレーションと関数
関数は、次のいずれかの前提条件が満たされるとシャードで実行されます。
-
関数はイミュータブルとして作成され、単一シャード最適化クエリに含まれます。
-
関数はユーザーによって配布されます
シャードで実行される関数は、データがある場所で実行されるため、パフォーマンスとスケーリングが向上します。
関数とボラティリティ
関数のボラティリティを確認するには、PostgreSQL のシステムテーブルで次のクエリを使用します。
SELECT DISTINCT nspname, proname, provolatile FROM pg_proc PRO JOIN pg_namespace NSP ON PRO.pronamespace = NSP.oid WHERE proname IN ('random', 'md5');
出力例:
nspname | proname | provolatile ------------+---------+------------- pg_catalog | md5 | i pg_catalog | random | v (2 rows)
この例で、md5() はイミュータブルで、random() は変動します。つまり、md5() を含む単一シャード最適化ステートメントは単一シャード最適化のままですが、random() を含むステートメントは最適化されません。
イミュータブル関数の例:
EXPLAIN ANALYZE SELECT pg_catalog.md5('123') FROM s1.t1 WHERE col_a = 776586194 AND col_b = 654849524 AND col_c = '3ac2f2affb02987159ccd6ebd23e1ae5';
QUERY PLAN
----------------------------------------------------
Foreign Scan (cost=100.00..101.00 rows=100 width=0)
(actual time=3.409..3.409 rows=1 loops=1)
Single Shard Optimized
Planning Time: 0.313 ms
Execution Time: 4.253 ms
(4 rows)
変動性のある関数の例:
EXPLAIN ANALYZE SELECT pg_catalog.random() FROM s1.t1 WHERE col_a = 776586194 AND col_b = 654849524 AND col_c = '3ac2f2affb02987159ccd6ebd23e1ae5';
QUERY PLAN ------------------------------------------------------ Foreign Scan on t1_fs00001 t1 (cost=100.00..15905.15 rows=1 width=8) (actual time=0.658..0.658 rows=1 loops=1) Planning Time: 0.263 ms Execution Time: 2.892 ms (3 rows)
出力は、md5() がプッシュダウンされて、単一シャード最適化として実行されていますが、random() はそうではないことを示しています。
関数の配布
1 つのシャードのみのデータにアクセスする関数は、パフォーマンス上の利点を得るために、そのシャードで実行する必要があります。関数を分散し、関数の署名に完全なシャードキーを含める必要があります。シャードキーのすべての列をパラメータとして関数に渡す必要があります。
関数の例
CREATE OR REPLACE FUNCTION s1.func1( param_a bigint, param_b bigint, param_c char(100) ) RETURNS int AS $$ DECLARE res int; BEGIN SELECT COUNT(*) INTO res FROM s1.t1 WHERE s1.t1.col_a = param_a AND s1.t1.col_b = param_b AND s1.t1.col_c = param_c; RETURN res; END $$ LANGUAGE plpgsql;
ディストリビューションの前は、関数の単一シャードの最適化は行われません。
EXPLAIN ANALYZE SELECT * FROM s1.func1(776586194, 654849524, '3ac2f2affb02987159ccd6ebd23e1ae5');
QUERY PLAN
------------------------------------------------------------------------------------------------------
Function Scan on func1 (cost=0.25..0.26 rows=1 width=4)
(actual time=37.503..37.503 rows=1 loops=1)
Planning Time: 0.901 ms
Execution Time: 51.647 ms
(3 rows)
関数を配布するには
SELECT rds_aurora.limitless_distribute_function( 's1.func1(bigint,bigint,character)', ARRAY['param_a','param_b','param_c'], 's1.t1' );
ディストリビューションの後、関数の単一シャードの最適化が行われます。
EXPLAIN ANALYZE SELECT * FROM s1.func1(776586194, 654849524, '3ac2f2affb02987159ccd6ebd23e1ae5');
QUERY PLAN
------------------------------------------------------------------------------------------------
Foreign Scan (cost=100.00..101.00 rows=100 width=0)
(actual time=4.332..4.333 rows=1 loops=1)
Single Shard Optimized
Planning Time: 0.857 ms
Execution Time: 5.116 ms
(4 rows)
rds_aurora.limitless_stat_statements の sso_calls 列を確認することで、単一シャードの最適化を確認できます。
subcluster_id | subcluster_type | calls | sso_calls | query --------------+-----------------+-------+-----------+-------------------------------------- 2 | router | 2 | 1 | SELECT * FROM s1.func1( $1, $2, $3 ) 3 | router | 1 | 1 | SELECT * FROM s1.func1( $1, $2, $3 ) (2 rows)
関数と効率のパターン
データの近くでロジックを実行する方が効率的であり、これを実現するには関数が重要な役割を果たします。関数の効率を向上させるには、主に 2 つのユースケースがあります。
-
複雑なデータからシャードキーを抽出して別の単一シャード最適化関数を呼び出す
-
クロスシャードロジックを単一シャード最適化ステートメントから分離して、クロスシャードワークロードを単一シャード最適化に変換する
複雑なデータからシャードキーを抽出する
いくつかのデータベースオペレーションを実行する署名 s3.func3(p_json_doc json) を持つ関数について検討します。これらのオペレーションは、すべてのシャードにまたがるトランザクション内のすべてのシャードで実行されます。JSON ドキュメントにシャードキーが含まれている場合、単一シャード最適化関数を構築してデータベースオペレーションを実行できます。
元のパターン:
s3.func3(p_json_doc json) database operation 1; database operation 2; database operation 3;
最適化パターン:
s3.func3(p_json_doc json) DECLARE v_a bigint; BEGIN v_a := (p_json_doc->>'field_a')::bigint; SELECT s3.func3_INNER(v_a, p_json_doc); END;
内部関数は次を実行します。
s3.func3_INNER(p_a, p_json_doc) database operation 1 WHERE shard_key = p_a; database operation 2 WHERE shard_key = p_a; database operation 3 WHERE shard_key = p_a;
このパターンでは、シャードキーは複雑なデータ型にカプセル化されるか、他のパラメータから推定されます。ロジック、データアクセス、関数は、シャードキーを決定、抽出、または構築し、単一シャードのみに関するオペレーションを実行する単一シャード最適化関数を呼び出すことができます。アプリケーションインターフェイスは変更されないため、最適化のテストは比較的簡単です。
他の関数またはデータからのシャードキーの遅延
ロジックまたはデータアクセスがシャードキーを計算または決定する場合、別の設計パターンが適用されます。これは、ほとんどの呼び出しで関数を単一のシャードで実行できますが、クロスシャード実行が必要になる場合があります。
元のパターン:
NEWORD(INTEGER, …) RETURNS NUMERIC DECLARE all_whid_local := true; LOOP through the order lines Generate warehouse ID; IF generated warehouse ID == input warehouse ID THEN ol_supply_whid := input warehouse ID; ELSE all_whid_local := false; ol_supply_whid := generated warehouse ID; END IF; … END LOOP; … RETURN no_s_quantity;
個別の関数で最適化されたパターン:
CREATE OR REPLACE FUNCTION NEWORD_sso(no_w_id INTEGER, …) RETURNS NUMERIC … RETURN no_s_quantity; … END; LANGUAGE 'plpgsql'; SELECT rds_aurora.limitless_distribute_function( 'NEWORD_sso(int,…)', ARRAY['no_w_id'], 'warehouse' ); CREATE OR REPLACE FUNCTION NEWORD_crosshard(no_w_id INTEGER, …) RETURNS NUMERIC … RETURN no_s_quantity; … END; LANGUAGE 'plpgsql';
次に、メイン関数で単一シャード最適化バージョンまたはクロスシャードバージョンを呼び出します。
IF all_whid_local THEN SELECT NEWORD_sso(…) INTO no_s_quantity; ELSE SELECT NEWORD_crosshard(…) INTO no_s_quantity; END IF;
このアプローチにより、ほとんどの呼び出しは、クロスシャード実行を必要とするケースで正しい動作を維持しながら、単一シャード最適化の恩恵を受けることができます。
単一シャードオペレーションの確認
EXPLAIN を使用して、ステートメントが単一シャード最適化されているかどうかを確認します。出力は、最適化されたオペレーションの「単一シャードの最適化」を明示的にレポートします。
ディストリビューション前のクロスシャード呼び出し:
QUERY PLAN
---------------------------------------------------------------------
Function Scan on func1 (cost=0.25..0.26 rows=1 width=4)
(actual time=59.622..59.623 rows=1 loops=1)
Planning Time: 0.925 ms
Execution Time: 60.211 ms
ディストリビューション後の単一シャード呼び出し:
QUERY PLAN
----------------------------------------------------------------------
Foreign Scan (cost=100.00..101.00 rows=100 width=0)
(actual time=4.576..4.577 rows=1 loops=1)
Single Shard Optimized
Planning Time: 1.483 ms
Execution Time: 5.404 ms
実行時間の違いは、単一シャード最適化のパフォーマンス上の利点を示しています。