ストアドプロシージャ パフォーマンス(以前の記事) および ユーザビリティ が疑わしいMySQLのバックグラウンドから来て、私は自分の会社の新製品についてPostgreSQLを評価しています。
私がやりたいことの1つは、いくつかの アプリケーションロジック をストアドプロシージャに移動することです。そのため、ここでは、PostgreSQL(9.0で関数を使用する場合のDOおよびDO N'T(ベストプラクティス)を求めています。 )、特にパフォーマンスの落とし穴について。
厳密に言うと、「ストアドプロシージャ」という用語は、Postgres 11で導入されたPostgresのSQLプロシージャを指します。
functionsもあり、ほとんど同じではありませんが、それらは最初からありました。
Functions with _LANGUAGE sql
_は基本的に、関数ラッパーのプレーンSQLコマンドを含む単なるバッチファイルです(したがって、アトミックで、常に single トランザクション内で実行されます)。パラメータを受け入れます。 SQL関数のすべてのステートメントは、一度に計画されます。これは、1つのステートメントを次々に実行することとは微妙に異なり、 ロックが取得される順序に影響する可能性があります。 =
それ以上のものについては、最も成熟した言語は PL/pgSQL (_LANGUAGE plpgsql
_)です。これはうまく機能し、過去10年間のすべてのリリースで改善されてきましたが、SQLコマンドの接着剤として最適です。 (SQLコマンド以外の)重い計算のためのものではありません。
PL/pgSQL関数は 準備されたステートメント のようなクエリを実行します。キャッシュされたクエリプランを再利用すると、いくつかの計画のオーバーヘッドが削減され、同等のSQLステートメントよりも少し高速になります。これは、状況によっては顕著な影響になる場合があります。また、この関連する質問のような副作用があるかもしれません:
これには、準備済みステートメントの長所と短所があります- マニュアルで説明されているように 。不規則なデータ分布とさまざまなパラメーターを持つテーブルのクエリの場合動的SQL with EXECUTE
は、指定されたパラメーターの最適化された実行プランから得られた場合にパフォーマンスが向上する可能性があります( s)再計画のコストを上回る。
Postgres 9.2以降、一般的な実行プランは引き続きセッションにキャッシュされますが、 マニュアルの引用 :
これは、パラメーターのない準備済みステートメントの場合、すぐに発生します。それ以外の場合は、5回以上の実行で、一般的な計画コスト見積もりよりも見積もりコスト平均(計画オーバーヘッドを含む)の方が高いプランが作成された後にのみ発生します。
EXECUTE
を(ab)使用せずに、ほとんどの場合(追加のオーバーヘッドを少し減らして)両方の世界のベストを手に入れます詳細は PostgreSQL WikiのPostgreSQL 9.2 の新機能 をご覧ください。
Postgres 12は、追加の サーバー変数_plan_cache_mode
_ を導入して、一般的な計画またはカスタム計画を強制します。特別な場合は注意して使用してください。
アプリケーションからデータベースサーバーへの追加のラウンドトリップを防止するサーバー側関数を使用して、win bigを実行できます。サーバーに可能な限り一度に実行させ、明確に定義された結果のみを返すようにしてください。
ネストを避ける複雑な関数、特にテーブル関数(_RETURNING SETOF record
_またはTABLE (...)
)の。関数は、クエリプランナーの最適化の障壁となるブラックボックスです。これらは外部クエリのコンテキストではなく個別に最適化されるため、計画は簡単になりますが、完全な計画にはならない場合があります。また、関数のコストと結果のサイズを確実に予測することはできません。
このルールの exception は単純なSQL関数(_LANGUAGE sql
_)であり、 "inlined"-いくつかの前提条件がある場合会った 。これでクエリプランナーがどのように機能するかについての詳細を読んでください Neil Conwayによるプレゼンテーション (高度なもの)。
PostgreSQLでは、関数は常に自動的に実行されます単一トランザクション内。それはすべて成功するか、何も起こらない。例外が発生すると、すべてがロールバックされます。しかし エラー処理 ...
これが、関数が not 正確に"stored procedure"である理由でもあります(この用語が誤解を招くように使用されることもあります)。 VACUUM
、 _CREATE INDEX CONCURRENTLY
_ または _CREATE DATABASE
_ のような一部のコマンドは、トランザクションブロック内では実行できないため、関数では使用できません。 (SQLプロシージャではまだ、Postgres 11ではまだです。それは後で追加される可能性があります。)
私は長年にわたって何千ものplpgsql関数を書いてきました。
いくつかのDO:
一般的に言って、アプリケーションロジックをデータベースに移動することは、それがより高速になることを意味します-結局のところ、データの近くで実行されます。
SQL言語関数 は、コンテキストの切り替えを必要としないため、他の言語を使用する関数よりも高速であると確信しています(ただし100%確実ではありません)。欠点は、手続き型ロジックが許可されないことです。
PL/pgSQL は組み込み言語の中で最も完成度が高く、機能が充実していますが、パフォーマンスのために [〜#〜] c [〜#〜] を使用できます(ただし、計算量の多い関数にのみメリットがあります)
Postgresqlでユーザー定義関数(UDF)を使用すると、非常に興味深いことができます。たとえば、使用できる言語は数十あります。組み込みのpl/sqlとpl/pgsqlはどちらも機能と信頼性が高く、サンドボックスメソッドを使用して、ユーザーが非常に危険なことをするのを防ぎます。 Cで記述されたUDFは、データベース自体と同じコンテキストで実行されるため、最高のパワーとパフォーマンスを提供します。ただし、バックエンドがクラッシュしたりデータが破損したりするなど、小さなミスでも大きな問題を引き起こす可能性があるため、それは火で遊ぶのと同じです。 pl/R、pl/Ruby、pl/Perlなどのカスタムpl言語を使用すると、データベース層とアプリ層の両方を同じ言語で作成できます。これは、UDFを作成するためにPerlプログラマーJavaまたはpl/pgsqlなど)を教える必要がないことを意味するため、便利です。
最後に、 pl/proxy 言語があります。このUDF言語を使用すると、スケーリングの目的で、数十以上のバックエンドpostgresqlサーバーでアプリケーションを実行できます。これはSkypeの優秀な人々によって開発され、基本的に貧しい人の水平スケーリングソリューションを可能にします。驚くほど簡単に書き込むこともできます。
さて、パフォーマンスの問題についてです。これは灰色の領域です。あなたは一人のためのアプリを書いていますか?それとも1,000人?または10,000,000のために?アプリを構築してUDFを使用する方法は、スケーリングの方法に依存します。何千人ものユーザー向けに作成している場合、主なことは、データベースへの負荷をできるだけ減らすことです。データベースに移動および戻されるデータの量を削減するUDFは、IOの負荷を削減するのに役立ちます。ただし、CPUの負荷が増加し始めると、問題になる可能性があります。一般的に、 IO負荷が最優先事項であり、次にCPUに過負荷をかけないようにUDFが効率的であることを確認することが重要です。