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