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


11

テーブルに新しい列を追加するのに問題があります。
数回実行しようとしましたが、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ビットの32個のnull許容列があるためMAXALIGN、HeapTupleHeaderは4バイトの長さです(検証されていません。その方法がわかりません)。

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

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

ALTER TABLE mytable ALTER myothercolumn SET NOT NULL;

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

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


1
列の型をバイナリ互換ではない別の型に変更すると、実際には新しい列が作成され、データがコピーされ、古い列が削除されます。ただし、SET NOT NULLタイプを変更するのではなく、制約を追加するだけです。ただし、制約はテーブルに対してチェックする必要があり、テーブル全体をスキャンする必要があります。9.4では、より弱いロックを取得することでこれらのケースの一部を改善していますが、それでもかなり重いです。
クレイグリンガー

1
パフォーマンスの低下を疑う前に、ALTER TABLEがロックを待機しているだけではないことを確認する必要があります。チェックした場合は、質問の中でそれを述べてください。
DanielVérité2014

クレイグとダニエルに感謝します。alterコマンドを実行すると、pg_stat_activityに「true」を待機した状態で表示されます。これは、ロックを待機していることを意味します!?それはチェックするための良い方法ですか?ちなみに、この変更を実行する前は、すべてがうまく

より良いビューのためにwiki.postgresql.org/wiki/Lock_dependency_informationでクエリを試してください。コミットを忘れる長引くトランザクション、または常にビジー状態を維持するこのテーブルでの重いアクティビティのいずれかがあります。
DanielVérité2014

dba.SEの方が適しているかもしれません。
Erwin Brandstetter 2014

回答:


8

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

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

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

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

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

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

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

  • ヒープタプルヘッダーの後に1バイトの空きがあり、23バイトを占めます。したがって、最大8列の行のnullビットマップは、追加コストなしで効果的に提供されます。表の9番目の列では、さらに64列を提供するために、別の(通常は8)バイトt_hoff進められMAXALIGNます。したがって、次の境界線は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にアップグレードすると役立つ場合があります。大きなテーブルとロックの詳細について、いくつかの改善が行われました。しかし、DBに問題がある場合は、まずそれを理解する必要があります。


2
それはほぼ確実にロックです。ただし、テストとして、いつでもテーブルのコピーを作成し、それを変更してみることができます。それほど時間がかからない場合は、問題となっているのは実際の変更ではないことがわかります。

説明アーウィンをありがとう。私はあなたが正しいと思います、それはロックの問題のようです。pg_stat_activityを確認すると、ALTERに「待機中」のtrueがあることがわかります。理解できないのは、ALTERがテーブルのロックを取得できない理由です。実行中のクエリが見つからない場合でも、取得できないようです。しかし、ALTERの実行が始まるとすぐに、他のすべてのクエリはそれが完了するのを待っています。したがって、このアクティビティは、ALTERが他のすべてのクエリをロックしていることを示しているようですが、ALTERがロックを取得しなかったことも示しています。よくわからないことがあると思います!?

@MatthieuVerrecchia:リチャードが提案したテストを試しましたか?
Erwin Brandstetter、2014

1
テーブルを(pg_dump-> pg_sqlで)新しいテーブルにクローンしました。新しい列が50ミリ秒で正しく追加され、ロックの問題が確認されます。ちなみに、ALTERが本当に標準的なdbアクティビティでロックを取得できない理由はまだわかりません。

1
@ErwinBrandstetter私はあなたの提案に従い、VACUUM、REINDEXを試しました。REINDEXもブロックされていたため、ロックを取得できませんでした。いくつかの調査の後、問題は私たちの考えよりも簡単でした。開いたトランザクションで<IDLE>が1週間残っていた問題は解決されました。おかげですべてについて、情報は非常に役に立ちました。
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.