web-dev-qa-db-ja.com

SQLAlchemyの「デフォルト」と「server_default」のパフォーマンス

PostgreSQLでSQLAlchemyを使用するときにテーブル列のデフォルト値をマッピングするためにserver_defaultの代わりにdefaultを使用すると、パフォーマンス上の利点(または欠点)がありますか?

私の理解では、 defaultINSERT(通常)の式をレンダリングし、 server_defaultCREATE TABLEステートメントに式を配置します。 server_defaultは、次のようなdbでのデフォルトの直接的な処理に類似しているようです。

CREATE TABLE example (
    id serial PRIMARY KEY,
    updated timestamptz DEFAULT now()
);

...しかし、INSERTまたはテーブル作成を介してデフォルトを処理する方が効率的かどうかは明確ではありません。

以下の例のdefaultパラメーターのそれぞれがserver_defaultに変更された場合、行挿入のパフォーマンスの向上または低下はありますか?

from uuid import uuid4
from sqlalchemy import Column, Boolean, DateTime, Integer
from sqlalchemy.dialects.postgresql import UUID
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.sql import func

Base = declarative_base()

class Item(Base):
    __tablename__ = 'item'

    id = Column(UUID(as_uuid=True), primary_key=True, default=uuid4)
    count = Column(Integer, nullable=False, default=0)
    flag = Column(Boolean, nullable=False, default=False)
    updated = Column(DateTime(timezone=True), nullable=False, default=func.now())

注:これまで見つけたserver_defaultの代わりにdefaultを使用する場合の最良の説明は、パフォーマンスに対応していません( Mike BayerのSOを参照)主題についての回答 )。その説明の私の単純化した要約は、defaultserver_defaultよりも優先されるということです...

  • Dbは、デフォルト値に必要な式または使用したい式を処理できません。
  • スキーマを直接変更することはできません。

...そのため、defaultserver_defaultのいずれかを選択する際にパフォーマンスを考慮する必要があるかどうかは疑問のままです。

14
benvc

デフォルト値式ごとのパフォーマンスは、サーバーとPythonの両方で大きく異なる可能性があるため、「これは速い」という答えを出すことは不可能です。現在時刻を取得する関数は、スカラーのデフォルト値とは異なる動作をします。

次に、デフォルトをfiveのさまざまな方法で提供できることを理解する必要があります。

  • クライアント側のスカラーのデフォルト0Trueなどの固定値。値はINSERTステートメントで使用されます。
  • クライアント側のPython function 。デフォルトが必要になるたびに呼び出され、挿入する値を生成し、それ以降はスカラーのデフォルトと同じ方法で使用します。これらは context sensitive にすることができます(挿入する値を使用して現在の実行コンテキストにアクセスできます)。
  • クライアント側SQL式 ;これにより、追加の SQL式 が生成され、クエリで使用され、サーバーで実行されて値が生成されます。
  • サーバー側のDLL expression はSQL式であり、テーブル定義に格納されるため、スキーマの一部です。サーバーはこれらを使用して、INSERTステートメントから省略された列の値を埋めます。または、DEFAULTまたはINSERTで列の値がUPDATEに設定されている場合ステートメント。
  • サーバー側の暗黙のデフォルトまたはトリガー 。トリガーや特定のデータベース機能などの他のDLLは列のデフォルト値を提供します。

デフォルト値を決定するSQL式に関しては、クライアント側のSQL式、サーバー側のDLL式、またはトリガーであっても、デフォルト値が存在するデータベースにはほとんど違いがないことに注意してください。表現はから来ています。クエリエグゼキューターは、特定の列の値を生成する方法を知る必要があります。DMLステートメントまたはスキーマ定義から解析されると、サーバーは行ごとに式を実行する必要があります。

これらのオプションを選択することは、パフォーマンスのみに基づくことはめったにありません。パフォーマンスは高々である必要がありますが、考慮すべき複数の側面の1つです。ここには多くの要因が含まれます。

  • defaultはスカラー関数またはPython関数で直接Pythonデフォルト値を生成し、挿入時に新しい値をサーバーに送信します。 Pythonコードは、データがデータベースに挿入される前にデフォルト値にアクセスできます。
  • クライアント側のSQL式、server_default値、およびサーバー側の暗黙的なデフォルトとトリガーはすべて、サーバーにデフォルトを生成させます。同じSQLAlchemyセッションでアクセスできるようにするには、クライアントがこれをフェッチする必要があります。オブジェクトがデータベースに挿入されるまで、値にアクセスできません。

    正確なクエリとデータベースのサポートに応じて、SQLAlchemyはextra SQLクエリを作成して、INSERTステートメントの前にデフォルトを生成するか、後で別のSELECTを実行する必要があります。挿入されたデフォルトを取得します。これをいつ実行するかを制御できます( eager_defaults mapper configuration を使用して、挿入時またはフラッシュ後の最初のアクセス時に直接)。

  • 同じデータベースにアクセスする異なるプラットフォーム上の複数のクライアントがある場合、server_defaultまたはスキーマに接続された他のデフォルト(トリガーなど)に関係なく、Pythonで実装されたデフォルトはすべてのクライアントが同じデフォルトを使用します他のプラットフォームからはアクセスできません。

PostgreSQLを使用する場合、SQLAlchemyは RETURNING句をDMLステートメント に使用できます。これにより、クライアントはサーバー側で生成されたデフォルトに1ステップでアクセスできます。

したがって、各行の新しい値(スカラー値ではない)を計算するserver_default列のデフォルトを使用する場合、Python側の時間を少し節約し、その列のデータを送信しないのでネットワーク帯域幅を少し節約しますデータベースへ。データベースcould同じ値の作成が高速になるか、低速になる可能性があります。それは主に操作の種類に依存します。同じトランザクションでPythonから生成されたデフォルト値にアクセスする必要がある場合、SQLAlchemyによって解析されたデータのリターンストリームを待つ必要があります。ただし、これらすべての詳細canは、行の挿入または更新で発生する他のすべてと比較して重要ではなくなります。

ORMが高性能なバルク行の挿入または更新に使用するのに適していない;であることを理解してください。 SQAlchemyからの引用パフォーマンス FAQエントリ

SQLAlchemy ORMは、データベースへの変更を同期するときに作業単位パターンを使用します。このパターンは、単純なデータの「挿入」をはるかに超えています。オブジェクトに割り当てられた属性は、オブジェクトの変更を追跡する属性インストルメンテーションシステムを使用して受信され、挿入されたすべての行はIDマップで追跡され、各行に対してSQLAlchemyが「最後に挿入されたID」がまだ指定されていない場合は、挿入される行が必要に応じて依存関係についてスキャンおよびソートされることも含まれます。また、オブジェクトはすべての実行を維持するためにかなりの程度のブックキーピングの対象となります。非常に多数の行を一度に使用すると、大きなデータ構造で膨大な時間を費やす可能性があるため、これらをチャンクすることをお勧めします。

基本的に、作業単位は、複雑なオブジェクトグラフを明示的な永続化コードなしでリレーショナルデータベースに永続化するタスクを自動化するための高度な自動化であり、この自動化にはコストがかかります。

ORMは基本的に高性能の一括挿入を目的としていません。これが、SQLAlchemyがORMに加えてコアを第一級コンポーネントとして提供する理由です。

SQLAlchemyのようなORMには多額の間接費が伴うため、サーバー側のデフォルトとPython側のデフォルトのパフォーマンスの違いは、ORM操作のノイズですぐに消えます。

したがって、大量の挿入操作または更新操作のパフォーマンスが心配な場合は、 bulk operations を使用し、 psycopg2バッチ実行ヘルパーを有効にする必要があります。 で実際に速度が向上します。これらの一括操作を使用する場合、行データをPythonからサーバーに移動する帯域幅を保存するだけで、サーバー側のデフォルトでパフォーマンスが向上すると予想しますが、その量はデフォルト値の正確な性質に依存します。

一括操作以外でのORMの挿入と更新のパフォーマンスが大きな問題である場合は、特定のオプションをテストするが必要です。まず、 SQLAlchemy examples.performance package および 独自のテストスイートを追加します 単一のserver_defaultdefault構成。

20
Martijn Pieters

2つのパフォーマンスを比較するだけでなく、他に重要なことがあります

新しい列create_at (Not Null)を既存のテーブルUserに追加する必要があり、データが含まれている場合、defaultは機能しません。

defaultを使用すると、データベースのアップグレード中に、テーブル内の既存のデータにNull値を挿入できないというエラーが発生します。また、テストだけのためにデータを維持したい場合、これは重大なトラブルを引き起こします。

また、server_defaultを使用すると、データベースのアップグレード中に、データベースは現在のDateTime値を以前のすべての既存のテストデータに挿入します。

したがって、この場合、server_defaultのみが機能します。