web-dev-qa-db-ja.com

nullable列をテーブルに追加すると10分以上かかる

テーブルに新しい列を追加するのに問題があります。
数回実行しようとしましたが、10分以上実行した後、ロック時間のためにクエリをキャンセルすることにしました。

ALTER TABLE mytable ADD mycolumn VARCHAR(50);

有用な情報:

  • PostgreSQLバージョン:9.1
  • 行数:〜250K
  • 列の数:38
  • Null許容列の数:32
  • 制約の数:5(1 PK、3 FK、1 UNIQUE)
  • インデックスの数:1
  • OSタイプ:Debian Squeeze 64

私は 興味深い情報 が(HeapTupleHeaderを介して)PostgreSQLがnull許容列を管理する方法について見つけました。

私の最初の推測は、このテーブルには既に8ビットMAXALIGNの32のnull許容列があるため、HeapTupleHeaderは4バイトの長さです(検証されていないため、その方法がわかりません)。

そのため、新しいnull可能列を追加するには、すべての行でHeapTupleHeaderを更新して新しい8ビットMAXALIGNを追加する必要があり、パフォーマンスの問題が発生する可能性があります。

そのため、null許容列の数を31に減らすために、null許容列の1つ(実際には実際にはnull許容ではありません)を変更して、私の推測が正しいかどうかを確認しようとしました。

ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;

残念ながら、この変更にも5分以上の非常に長い時間がかかるため、中止しました。

このパフォーマンスコストの原因は何か考えていますか?

11

ここにはいくつかの誤解があります:

nullビットマップは、ヒープタプルヘッダーのnot部分ではありません。 ドキュメントごと:

固定サイズのヘッダー(ほとんどのマシンで23バイトを占める)があり、その後にオプションのnullビットマップが続きます...

32のnull許容列は、次の2つの理由で不審ではありません。

  • Nullビットマップは、行ごとに追加されます。ただし、実際のNULLが少なくとも1つある場合のみ行の値Nullable列には直接的な影響はなく、実際のNULL値のみが影響します。ヌルビットマップが割り当てられている場合、常に完全に割り当てられます(全部またはまったく)。 nullビットマップの実際のサイズは、列ごとに1ビットで、次のバイトに切り上げられます。 現在のソースコードごと:

    #define BITMAPLEN(NATTS) (((int)(NATTS) + 7) / 8)
    
  • Nullビットマップは、ヒープタプルヘッダーの後に割り当てられ、その後にオプションのOIDが続き、その後行データが続きます。OIDまたは行データの開始は、ヘッダーのt_hoffコメントごとのソースコード

    T_hoffはMAXALIGNの倍数でなければならないことに注意してください。

  • ヒープタプルヘッダーの後に1バイトの空きがあり、23バイトを占めます。したがって、8列までの行のnullビットマップは、追加コストなしで効果的に使用できます。表の9列目では、 t_hoff がさらにMAXALIGN(通常は8)バイト進んで、さらに64列を提供します。したがって、次の境界線は72列になります。

PostgreSQLデータベースクラスター(MAXALIGNを含む)の制御情報を表示するには、DebianマシンへのPostgres 9.3の一般的なインストールの例:

    Sudo /usr/lib/postgresql/9.3/bin/pg_controldata /var/lib/postgresql/9.3/main

引用した関連回答の説明 を更新しました。

さて、あなたのALTER TABLEステートメントがテーブル全体の書き換えをトリガーしたとしても(おそらくデータタイプを変更します)、250Kは実際にはそれほど大きくなく、中間の適切なマシンでは数秒で済みます(ただし、行が異常に大きい)。 10分以上は、まったく異なる問題を示します。ステートメントは、おそらくテーブルのロックを取得するために待機しています。

pg_stat_activityのエントリ数の増加は、より多くの開いているトランザクションを意味します-操作の終了を待機する必要があるテーブルへの同時アクセス(ほとんどの場合)を示します。

暗闇の中でいくつかのショット

可能なテーブル膨張を確認し、穏やかなVACUUM mytableまたはより積極的なVACUUM FULL mytableを試してください。このフォームも排他ロックを取得するため、同じ同時実行の問題が発生する可能性があります。代わりに pg_repack を試すことができます...

最初に、インデックス、トリガー、外部キー、またはその他の制約、特に列に関連する制約について考えられる問題を調べます。特に破損したインデックスが含まれる可能性がありますか?それらすべてのREINDEX TABLE mytable;またはDROPを試し、ALTER TABLEの後に同じトランザクションの後に再度追加してください。

夜間または負荷が少ないときにコマンドを実行してみてください。

ブルートフォースの方法は、サーバーへのアクセスを停止して、再試行することです。

それをピン留めすることができずに、現在のバージョンまたは特に次の9.4にアップグレードすると、mightヘルプが役立ちます。大きなテーブルとロックの詳細について、いくつかの改善が行われました。しかし、DBに問題がある場合は、まずそれを理解する必要があります。

8