web-dev-qa-db-ja.com

大きなテーブルでのUPDATEパフォーマンスを改善する

Amazon RDS(2vCPU、8 GB RAM)でPostgres 9.5を使用しています。
私はpganalyzeを使用して自分のパフォーマンスを監視しています。
データベースに約200Kのレコードがあります。

私のダッシュボードでは、次のクエリの実行に平均28秒と11秒かかっています。

UPDATE calls SET ... WHERE calls.uuid = ?   telephonist 28035.41    0.01    100%    0.03%

UPDATE calls SET sip_error = ? WHERE calls.uuid = ? telephonist 11629.89    0.44    100%    0.69%

私はすでにVACUUMを試し、7,670の死んだ行を見つけて削除しました。
UPDATEのパフォーマンスを向上させる方法はありますか?これはクエリです:

UPDATE calls SET X=Y WHERE calls.uuid = 'Z'

上記のクエリをどのように改善できますか?別のフィールドを追加できますか?例:

UPDATE calls SET X=Y WHERE calls.uuid = 'Z' AND calls.campaign = 'W'

uuidはインデックス付けされていません。
https://www.tutorialspoint.com/postgresql/postgresql_indexes.htm は、UPDATE操作にはインデックスを推奨しないことを示唆しています。

CREATE TABLE public.calls (
    id int4 NOT NULL DEFAULT nextval('calls_id_seq'::regclass),
    callsid varchar(128),
    call_start timestamp(6) NOT NULL,
    call_end timestamp(6) NULL,
    result int4 DEFAULT 0,
    destination varchar(256),
    campaign varchar(128),
    request_data varchar(4096),
    uuid varchar(128) NOT NULL,
    status varchar(64),
    duration int4,
    recording_file varchar(256),
    recording_url varchar(256),
    recording_duration int4,
    recording_text varchar(4096),
    recording_download bool DEFAULT false,
    description varchar(4096),
    analysis varchar(4096),
    is_fax bool DEFAULT false,
    is_test bool,
    hangup_cause varchar(128),
    media_detected bool DEFAULT false,
    sip_callid varchar(256),
    hangup_cause_override varchar(256),
    is_blacklisted bool DEFAULT false,
    sip_error varchar(256),
    hangup_cause_report varchar(128),
    summary varchar(1024)
);
EXPLAIN ANALYZE 
    SELECT * FROM calls 
    WHERE calls.uuid='e2ce9eb4-v1lp-p14u-7kkk-lruy-e2ceaae46d';
コールのシーケンススキャン(コスト= 0.00..16716.25行= 1幅= 3301)
(実際の時間= 81.637..81.637行= 0ループ= 1)
フィルター: ((uuid):: text = 'e2ce9eb4-v1lp-p14u-7kkk-lruy-e2ceaae46d' :: text)
フィルターによって削除された行:99970 
計画時間:0.482 ms 
実行時間:81.683ミリ秒
5
californian

仮定uuidUNIQUEであると想定されています。このテーブル定義は、スペースを節約し、パフォーマンスを向上させるはずです。

CREATE TABLE public.calls (
   id serial PRIMARY KEY,
   result int4 DEFAULT 0 NOT NULL,
   uuid uuid UNIQUE NOT NULL  -- creates the index you need automatically
   call_start timestamp NOT NULL,
   call_end timestamp,  -- so this can be NULL?
   duration int4,
   recording_duration int4,
   callsid varchar(128),
   destination varchar(256),
   campaign varchar(128),
   request_data varchar(4096),
   status varchar(64),
   recording_file varchar(256),
   recording_url varchar(256),
   recording_text varchar(4096),
   recording_download bool DEFAULT false,
   description varchar(4096),
   analysis varchar(4096),
   is_fax bool DEFAULT false,
   is_test bool,
   hangup_cause varchar(128),
   media_detected bool DEFAULT false,
   sip_callid varchar(256),
   hangup_cause_override varchar(256),
   is_blacklisted bool DEFAULT false,
   sip_error varchar(256),
   hangup_cause_report varchar(128),
   summary varchar(1024)
);

ここで最も重要な機能はUNIQUE制約です。これは一意のindexで実装されており、インデックスは必要以上のものですその他( @ ypercubeはコメント済み など)。

uuidが一意でない場合は、プレーンなbtreeインデックスを作成します。

uuidが有効なuuidでない場合は、文字タイプ(varcharまたはtext)のままにしますが、そのインデックスを作成します。

データ型varcharuuidのサイズとパフォーマンスに関する考慮事項:

私の他の提案された変更はすべてマイナーな改善です。詳細な説明はこちら:

特定の最大長を強制する必要がない場合は、すべての文字列にtextを使用します。ただし、それがパフォーマンスに直接影響することはほとんどありません。一部の列は、より適切なタイプに変換される場合があります(実際のパフォーマンス上の利点があります)。


インデックスとUPDATE

では、なぜ そのチュートリアルページ と言うのですか。

いつインデックスを回避すべきですか?
[...]
-頻繁な大規模なバッチ更新または挿入操作があるテーブル。

それは省略によって誤解を招くものです。あなた必死に更新の述語をサポートするには、uuidに1つのインデックスが必要です。他のすべてのインデックスは、更新後にそれらを最新に保つために追加の作業を必要とするため、更新が遅くなります。したがって、PRIMARY KEY on id(たとえば、FK制約を許可するため)をドロップできます(代わりにuuidをPKにする-自動的にインデックスが作成されます)。

7