タスクは難解に思えるかもしれませんが、それでも、ある種のPOCを作成したいと思います。
私の目標は、PostgreSQLデータベース(バージョン10)を使用するアプリケーションにAPIを公開することです。
APIは、UDFのセットの形式である必要があります。すべての関数は、アプリケーションにアクセスできる唯一のパブリックスキームに属しています。テーブルやその他のものはプライベートスキームに隠されています。 オブジェクト指向データベースとほとんど同じです。
これが私がそれを機能させようとしている理由です:
まあ、気にしないでください。
したがって、いくつかの非常に単純な関数(テーブルからすべてのレコードを取得するなど)を作成しようとしたとき、それらは常に、ラップするクエリよりも遅いと述べました。これは完全に受け入れ可能であり、それ自体で理解可能ですが、タイミングの違いは非常に大きくなる可能性があります。したがって、受け入れられません。
このようなテーブルがあります。
CREATE TABLE notifications (
id SERIAL PRIMARY KEY,
source_type INTEGER NOT NULL,
content JSONB,
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP(3)
)
そして、120,000以上のレコード。
それらをすべて取得したいとします。
ここでは、単純なクエリを使用して実行します。インデックスはありません。JSONBデータはすべてのレコードでほぼ1kbです。
EXPLAIN (ANALYZE,VERBOSE,BUFFERS) SELECT * FROM private.notifications;
QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------------------
Seq Scan on private.notifications (cost=0.00..16216.13 rows=120113 width=877) (actual time=0.015..496.473 rows=120113 loops=1)
Output: id, source_type, content, created
Buffers: shared hit=15015
Planning time: 0.063 ms
Execution time: 973.935 ms
496ms。
では、次のようなpl/pgsql関数を利用してみましょう。
CREATE OR REPLACE FUNCTION notifications_get()
RETURNS SETOF private.notifications AS
$$
BEGIN
RETURN QUERY SELECT * from private.notifications;
END
$$
LANGUAGE 'plpgsql'
SECURITY DEFINER;
EXPLAIN (ANALYZE,VERBOSE,BUFFERS) SELECT * FROM notifications_get();
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------------------
Function Scan on notifications_get (cost=0.25..10.25 rows=1000 width=48) (actual time=99.561..589.129 rows=120113 loops=1)
Output: id, source_type, content, created
Function Call: notifications_get()
Buffers: shared hit=15015
Planning time: 0.045 ms
Execution time: 1091.698 ms
589ms。
明らかに、関数とクエリの違いは、最初のレコードの取得に費やされたこれらの99.5msです。
さらに最適化を試みました(たぶん素朴かもしれません):
私が直面したもう1つの問題は、IDでレコードを取得する関数にありました。 0.25ms vs 0.025ms。違いは10倍ですが、多かれ少なかれ、それはどこから来たのかを理解しています。繰り返しますが、上記の最適化のトリックで違いが生じたわけではありません(そうすべきではないようです)。
これは問題の関数と(ほぼ)同等ですが、単純なSELECT
のように機能します。
CREATE OR REPLACE FUNCTION notifications_get_faster()
RETURNS SETOF private.notifications AS
$func$
SELECT * FROM private.notifications
$func$ LANGUAGE sql STABLE;
ほぼ、これはSECURITY DEFINER
ではないため、望ましい効果が得られません。
特に、クエリプランにはSeq Scan
ではなくFunction Scan
が表示されます。それが違いのほとんどを作るものです。
テーブル関数のインライン化 のall条件を満たしていません。 。この機能はありません。特に:
関数は
LANGUAGE SQL
です関数は
SECURITY DEFINER
ではありません関数が
STABLE
またはIMMUTABLE
として宣言されている
そのため、Postgresは関数本体を受け取り、関数のオーバーヘッドなしで実行できます(「関数のインライン化」)。プレーンなSELECT
と比較して、ごくわずかな追加の計画コストが追加されるだけです。
余談ですが、言語名を引用しないでください。識別子です。