2013-08-18 11 views
14

私はpostgres 9.1で作業しており、単純な更新メソッドを過剰に実行するとデッドロック例外が発生します。単純な更新クエリのpostgresのデッドロック

ログによれば、同時に2つの同一の更新が実行されるため、デッドロックが発生します。

更新が設定public.vm_action_info last_on_demand_task_id = $ 1、バージョン=バージョン+ 2回の同一の単純な更新が互いをデッドロックすることができますどのように1

私は、ログ

2013-08-18 11:00:24 IDT HINT: See server log for query details. 
2013-08-18 11:00:24 IDT STATEMENT: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 
2013-08-18 11:00:25 IDT ERROR: deadlock detected 
2013-08-18 11:00:25 IDT DETAIL: Process 31533 waits for ShareLock on transaction 4228275; blocked by process 31530. 
     Process 31530 waits for ExclusiveLock on tuple (0,68) of relation 70337 of database 69205; blocked by process 31533. 
     Process 31533: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 
     Process 31530: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 
2013-08-18 11:00:25 IDT HINT: See server log for query details. 
2013-08-18 11:00:25 IDT STATEMENT: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 
2013-08-18 11:00:25 IDT ERROR: deadlock detected 
2013-08-18 11:00:25 IDT DETAIL: Process 31530 waits for ExclusiveLock on tuple (0,68) of relation 70337 of database 69205; blocked by process 31876. 
     Process 31876 waits for ShareLock on transaction 4228275; blocked by process 31530. 
     Process 31530: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 
     Process 31876: update public.vm_action_info set last_on_demand_task_id=$1, version=version+1 where id=$2 

のスキーマを取得していますエラーは次のとおりです。

CREATE TABLE vm_action_info(
    id integer NOT NULL, 
    version integer NOT NULL DEFAULT 0, 
    vm_info_id integer NOT NULL, 
last_exit_code integer, 
    bundle_action_id integer NOT NULL, 
    last_result_change_time numeric NOT NULL, 
    last_completed_vm_task_id integer, 
    last_on_demand_task_id bigint, 
    CONSTRAINT vm_action_info_pkey PRIMARY KEY (id), 
    CONSTRAINT vm_action_info_bundle_action_id_fk FOREIGN KEY (bundle_action_id) 
     REFERENCES bundle_action (id) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE CASCADE, 
    CONSTRAINT vm_discovery_info_fk FOREIGN KEY (vm_info_id) 
     REFERENCES vm_info (id) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE CASCADE, 
    CONSTRAINT vm_task_last_on_demand_task_fk FOREIGN KEY (last_on_demand_task_id) 
     REFERENCES vm_task (id) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE NO ACTION, 

    CONSTRAINT vm_task_last_task_fk FOREIGN KEY (last_completed_vm_task_id) 
     REFERENCES vm_task (id) MATCH SIMPLE 
     ON UPDATE NO ACTION ON DELETE NO ACTION 
) 
WITH (OIDS=FALSE); 

ALTER TABLE vm_action_info 
    OWNER TO vadm; 

-- Index: vm_action_info_vm_info_id_index 

-- DROP INDEX vm_action_info_vm_info_id_index; 

CREATE INDEX vm_action_info_vm_info_id_index 
    ON vm_action_info 
    USING btree (vm_info_id); 

CREATE TABLE vm_task 
(
    id integer NOT NULL, 
    version integer NOT NULL DEFAULT 0, 
    vm_action_info_id integer NOT NULL, 
    creation_time numeric NOT NULL DEFAULT 0, 
    task_state text NOT NULL, 
    triggered_by text NOT NULL, 
    bundle_param_revision bigint NOT NULL DEFAULT 0, 
    execution_time bigint, 
    expiration_time bigint, 
    username text, 
    completion_time bigint, 
    completion_status text, 
    completion_error text, 
    CONSTRAINT vm_task_pkey PRIMARY KEY (id), 
    CONSTRAINT vm_action_info_fk FOREIGN KEY (vm_action_info_id) 
    REFERENCES vm_action_info (id) MATCH SIMPLE 
    ON UPDATE NO ACTION ON DELETE CASCADE 
) 
WITH (
OIDS=FALSE 
); 
ALTER TABLE vm_task 
    OWNER TO vadm; 

-- Index: vm_task_creation_time_index 

-- DROP INDEX vm_task_creation_time_index  ; 

CREATE INDEX vm_task_creation_time_index 
    ON vm_task 
    USING btree 
(creation_time); 
+0

彼らはそれほど単純ではありません。フィールドにはFKのconstantがあります(インデックスを更新する必要があります)。遅延可能なフィーリングが最初に延期されましたか? (相違が生じないとは思わない) – wildplasser

+1

FK制約を変更したくないのは、私がgenralのシステムにどのような影響を与えるのか完全にはわからないからです。一度に1つのクエリしか実行できないという制限をコードに追加することで、この問題は解決されますが、クエリがどのようにしてデッドロックを引き起こすかを理解できません。すべてのロックは同じ順序で取得されるので、実際には起こらないはずです。ポストグルが実際には存在しないデッドロックを誤って検出する可能性はありますか? – moshe

+0

あなたは 'すべてのロックが同じ順序で取得されました 'と書いていますが、単純な更新だけではなく、この単一の更新よりも多くのロックコマンドで構成されています。はいの場合は、コード全体を表示してください。 – krokodilko

答えて

4

それはちょうどあなたのシステムは非常に忙しかったということかもしれません。これは、クエリの「過剰な実行」でしか見ていないと言います。状況と思われるもの

はこれです:

pid=31530 wants to lock tuple (0,68) on rel 70337 (vm_action_info I suspect) for update 
    it is waiting behind pid=31533, pid=31876 
pid=31533 is waiting behind transaction 4228275 
pid=31876 is waiting behind transaction 4228275 

そう - 我々は、すべて同時に、この行を更新4回の取引であるように思わものを持っています。トランザクション4228275はまだコミットまたはロールバックされておらず、他のものを保持しています。そのうちの2人がdeadlock_timeout秒を待っていました。そうでなければ、タイムアウトは表示されませんでした。 Timoutが期限切れになると、デッドロック検出器が見られ、絡み合ったトランザクションが多数見られ、そのうちの1つが取り消されます。厳密にデッドロックではないかもしれませんが、検出器がそれを理解するのに十分なほどスマートであるかどうかはわかりません。

はおそらく#3を設定したい場合があります:-)最も簡単ですDEADLOCK_TIMEOUT

  • が速く、サーバーに
  • 増加を取得し、更新率を下げ

    1. のいずれかをお試しくださいlog_lock_waitsもありますので、あなたのシステムがこの種の緊張のもとにいるかどうかを/見ることができます。

  • +0

    遅いアップデート速度このシナリオは起こりません。 #3の提案について:postgresの文書によると、deadloak_timeoutパラメータは、デッドロック検出メカニズムが実行されるまでの時間を定義するだけで、デッドロック状況が宣言されているかどうかには影響しません。ドキュメンテーションから: "彼はデッドロック状態があるかどうかを確認する前に、ロックを待つ時間をミリ秒で表しています。デッドロックのチェックは比較的高価です。そのため、サーバーは毎回それを実行しませんロックを待つ」 – moshe

    +0

    バージョン9へのアップグレード。2も役立つかもしれませんが、ロック動作と全体的なスピードにいくつかの改善があります。 –

    +0

    トランザクションが中止されない限り、条件はデッドロックです。 –

    14

    私の推測では、問題の原因はテーブル内の循環外部キー参照です。

    表vm_action_info
    ==> FOREIGN KEY(last_completed_vm_task_id)REFERENCES vm_task(ID)

    表vm_task
    ==(ID)

    トランザクションは、2つのステップで構成さvm_action_info> FOREIGN KEY(vm_action_info_id)参照。

    1. vm_action_inのエントリを対応する
    2. アップデートタスクテーブルに新しいエントリを追加fo vm_taskテーブル。

    2つのトランザクションが同時にvm_action_infoテーブル内の同じレコードを更新しようとしているときに、これはデッドロックを終了します。単純なテストケースで

    ルック:

    CREATE TABLE vm_task 
    (
        id integer NOT NULL, 
        version integer NOT NULL DEFAULT 0, 
        vm_action_info_id integer NOT NULL, 
        CONSTRAINT vm_task_pkey PRIMARY KEY (id) 
    ) 
    WITH (OIDS=FALSE); 
    
    insert into vm_task values 
    (0, 0, 0), (1, 1, 1), (2, 2, 2); 
    
    CREATE TABLE vm_action_info(
        id integer NOT NULL, 
        version integer NOT NULL DEFAULT 0, 
        last_on_demand_task_id bigint, 
        CONSTRAINT vm_action_info_pkey PRIMARY KEY (id) 
    ) 
    WITH (OIDS=FALSE); 
    insert into vm_action_info values 
    (0, 0, 0), (1, 1, 1), (2, 2, 2); 
    
    alter table vm_task 
    add CONSTRAINT vm_action_info_fk FOREIGN KEY (vm_action_info_id) 
        REFERENCES vm_action_info (id) MATCH SIMPLE 
        ON UPDATE NO ACTION ON DELETE CASCADE 
        ; 
    Alter table vm_action_info 
    add CONSTRAINT vm_task_last_on_demand_task_fk FOREIGN KEY (last_on_demand_task_id) 
         REFERENCES vm_task (id) MATCH SIMPLE 
         ON UPDATE NO ACTION ON DELETE NO ACTION 
         ; 
    


    セッション1において、我々は、セッション2 ANで同時にvm_action_info

    session1=> begin; 
    BEGIN 
    session1=> insert into vm_task values(100, 0, 2); 
    INSERT 0 1 
    session1=> 
    

    にID = 2への参照をvm_taskにレコードを追加します

    session2=> begin; 
    BEGIN 
    session2=> insert into vm_task values(200, 0, 2); 
    INSERT 0 1 
    session2=> 
    

    次に、最初のトランザクションはupdを実行します

    session1=> update vm_action_info set last_on_demand_task_id=100, version=version+1 
    session1=> where id=2; 
    

    が、このコマンドがハングし、ロックを待機している.....

    その後、第二セッションは更新を実行........

    session2=> update vm_action_info set last_on_demand_task_id=200, version=version+1 where id=2; 
    BŁĄD: wykryto zakleszczenie 
    SZCZEGÓŁY: Proces 9384 oczekuje na ExclusiveLock na krotka (0,5) relacji 33083 bazy danych 16393; zablokowany przez 380 
    8. 
    Proces 3808 oczekuje na ShareLock na transakcja 976; zablokowany przez 9384. 
    PODPOWIEDŹ: Przejrzyj dziennik serwera by znaleźć szczegóły zapytania. 
    session2=> 
    

    デッドロック:食べました検出されました!

    これは、vm_taskへの両方のINSERTが、外部キー参照のためにvm_action_infoテーブルの行ID = 2に共有ロックを設定するためです。最初の更新では、この行に書き込みロックを設定しようとし、行が別の(2番目の)トランザクションによってロックされているためハングします。次に、2番目の更新は、同じレコードを書き込みモードでロックしようとしますが、最初のトランザクションによって共有モードでロックされます。そして、これはデッドロックを引き起こします。

    私は、これはあなたがvm_action_info内のレコードの書き込みロックを配置する場合、トランザクション全体は5つのステップで構成されなければならない回避することができると思う:

    begin; 
    select * from vm_action_info where id=2 for update; 
    insert into vm_task values(100, 0, 2); 
    update vm_action_info set last_on_demand_task_id=100, 
         version=version+1 where id=2; 
    commit; 
    
    関連する問題