プロンプトと範囲
ある注文サービスに unit_price、quantity、discount があり、同一行のみから導出される net_amount が必要です。面接官は、インデックス、論理レプリケーション、ロールバック、および古いサブスクライバーをデータベースがサポートしなければならない状況で、書き込み時に計算すべきか、それとも読み取りのたびに計算すべきかを尋ねています。中核となるスキルは構文を思い出すことではなく、永続化の境界を判断することです。
面接官が評価するポイント
VIRTUALの読み取り時計算と、STOREDの書き込み時計算およびストレージコストを区別できているか。- 式が現在の行、イミュータブル(不変)関数、およびサポート対象の型のみを使用しているかを確認しているか。
- 仮想列ではユーザー定義型や関数を使用できないのに対し、格納列では制限が少ないことを把握しているか。
- クエリの実行頻度、書き込み量、インデックス作成、およびレプリケーション構成に基づいて選択しているか。
- PostgreSQL 18 のパブリッシャーと古いサブスクライバーとの間に、明確な互換性とロールバックのパスが存在するか。
最初に確認すべき明確化のための質問
net_amountは、頻繁なフィルタリング、ソート、または一意性制約に使用されますか?インデックスが必要な場合、通常は書き込み時の実体化が有利になります。- ワークロードは読み取り重視ですか、それとも書き込み重視ですか?仮想列はストレージを節約できますが、読み取りごとに計算が発生します。
- 論理レプリケーションのサブスクライバーは PostgreSQL 18 ですか?古いバージョンでは、初期同期時に生成列がコピーされません。
- 式がユーザー定義関数、外部テーブル、または現在時刻に依存する可能性はありますか?それにより決定性とサポート状況が変わります。
30秒の回答フレームワーク
まず計算式と整合性の境界を確定させます。シンプルで読み取り頻度が低く、物理レプリカを必要としない値であれば、VIRTUAL を使用して PostgreSQL に読み取り時の計算を任せます。値に安定したインデックス、読み取り CPU 負荷の低減が必要な場合や、サブスクライバー側で計算済みの値を受け取る必要がある場合は、STORED を使用します。式のイミュータビリティとバージョンサポートを検証し、パブリッシャー、サブスクライバー、インデックス、ロールバックをテストした上で、本番に近いデータを用いて読み取りレイテンシ、書き込み増幅、レプリケーションの挙動を比較します。
段階的な推論
PostgreSQL 18 では VIRTUAL が生成列のデフォルト種別となります。行ストレージを消費せず、読み取り時に計算されます。STORED は挿入または更新時に計算され、ストレージを消費します。どちらの種別も INSERT や UPDATE で直接値を代入することはできません。式は現在の行とイミュータブル関数のみを参照できます。
判断基準は、ワークロードの影響を受けにくい部分にコストを逃がすことです。頻繁な読み取り、インデックス、または結果をそのまま利用すべきレプリカがある場合は STORED が有利であり、容量と書き込み CPU を引き換えにして安定した読み取り性能を得ます。書き込み頻度が高く短い式で読み取りが少ない場合は VIRTUAL が適しており、ストレージと書き込み処理を抑えて読み取り CPU を使います。仮想列は、名前にキャッシュのような響きがあっても、共有結果キャッシュではありません。
SQL とレプリケーションの例
この例では明示的に金額を格納しています。STORED を VIRTUAL に変更すると、読み取り時に式が再評価されます。
CREATE TABLE order_line (
id bigint PRIMARY KEY,
unit_price numeric(12, 2) NOT NULL,
quantity integer NOT NULL CHECK (quantity > 0),
discount numeric(5, 4) NOT NULL CHECK (discount BETWEEN 0 AND 1),
net_amount numeric(12, 2)
GENERATED ALWAYS AS (unit_price * quantity * (1 - discount)) STORED
);
CREATE INDEX order_line_net_amount_idx ON order_line (net_amount);
CREATE PUBLICATION order_pub
FOR TABLE order_line
WITH (publish_generated_columns = 'stored');パブリッシャーは格納生成列を発行するように設定できます。仮想列には同じパスを介してコピーすべき物理的な値が存在しません。サブスクライバーが PostgreSQL 18 未満の場合、パブリッシャー側でオプションを有効にしていても初期同期で生成列はコピーされないため、サブスクライバー側での再計算またはフォールバック計画が必要です。
マイグレーション、インデックス、および障害パス
実際のデータ分布を使用して、シャドウテーブルで両方の選択肢をテストします。書き込みレイテンシ、読み取り CPU、インデックスサイズ、レプリカの追従時間を比較します。新しい値は通常の二重書き込み列として導入し、整合性を確認した後にのみ生成列へ切り替えます。最も混雑する時間帯に大規模テーブルを変更してはいけません。
バージョンが混在するレプリケーション構成では、パブリッシャーの設定、サブスクライバーのバージョン、および初期同期状態を記録します。値の不一致が発生した場合は、その列に依存するダウンストリームのコンシューマーを一時停止し、レプリケーションの欠落をゼロとして扱うのではなくベース列から再計算します。サンプリングおよび完全な一致チェックに合格するまでは、ロールバックに備えて元の列と式のバージョンを保持します。
よくある間違い
VIRTUALをキャッシュとみなしてしまい、読み取りごとに計算されることを忘れる。- すべての式で現在時刻、サブクエリ、またはユーザー定義関数が呼び出せると思い込む。
- バージョンやインデックスのサポートを確認せずに、インデックスパスに仮想列を選択する。
- パブリッシャーのみをアップグレードし、古いサブスクライバーの初期同期の挙動を無視する。
- レプリケーションやロールバックで値を再計算できなくなるように元列を削除してしまう。
フォローアップの質問
どのような場合に VIRTUAL を優先しますか?
短い式、限定的な読み取り頻度、頻繁な書き込みがあり、物理インデックスが不要な場合に優先します。本番展開の前に、読み取り CPU、テールレイテンシ、並行読み取りテストを用いて、ストレージの節約が許容できない計算コストにつながらないことを証明します。
STORED が必須となるのはどのような場合ですか?
値にインデックス、一意性チェック、レプリカでの安定した読み取り、あるいは安全に再計算できないサブスクライバーが必要な場合に使用します。値を書き込み監査の対象に含め、式の変更はデータマイグレーションとして扱います。
バージョンが混在するレプリケーション構成はどのようにアップグレードしますか?
パブリッシャーとサブスクライバーのバージョンを棚卸しします。古いサブスクライバーはベース列からの再計算、またはレプリケーションされる通常の移行列にとどめておきます。アップグレードして初期同期が完了した後に publish_generated_columns を有効にします。チェックが失敗した場合のロールバックパスを用意した上で、行数、ハッシュ、およびサンプリングした金額を照合します。