sql >> データベース >  >> RDS >> PostgreSQL

複数のコンシューマーを持つSQLテーブルとしてのジョブキュー(PostgreSQL)

    FIFOキューにもpostgresを使用しています。私はもともとACCESSEXCLUSIVEを使用しました。これは、高い同時実行性で正しい結果をもたらしますが、実行中にACCESSSHAREロックを取得するpg_dumpと相互に排他的であるという不幸な効果があります。これにより、next()関数が非常に長い時間(pg_dumpの期間)ロックされます。私たちは24時間年中無休のショップであり、顧客は深夜の待ち行列のデッドタイムを気に入らなかったため、これは受け入れられませんでした。

    pg_dumpの実行中はロックされず、同時に安全であり、制限の少ないロックが必要であると考えました。私の検索により、このSO投稿にたどり着きました。

    それから私はいくつかの調査をしました。

    キューに入れられたからジョブのステータスを更新するFIFOキューNEXT()関数には、次のモードで十分です。 実行中 同時実行性がない場合は失敗し、pg_dumpに対してもブロックされません:

    SHARE UPDATE EXCLUSIVE
    SHARE ROW EXCLUSIVE
    EXCLUSIVE
    

    クエリ:

    begin;
    lock table tx_test_queue in exclusive mode;
    update 
        tx_test_queue
    set 
        status='running'
    where
        job_id in (
            select
                job_id
            from
                tx_test_queue
            where
                status='queued'
            order by 
                job_id asc
            limit 1
        )
    returning job_id;
    commit;
    

    結果は次のようになります:

    UPDATE 1
     job_id
    --------
         98
    (1 row)
    

    これは、高い同時実行性(30)でさまざまなロックモードをすべてテストするシェルスクリプトです。

    #!/bin/bash
    # RESULTS, feel free to repro yourself
    #
    # noLock                    FAIL
    # accessShare               FAIL
    # rowShare                  FAIL
    # rowExclusive              FAIL
    # shareUpdateExclusive      SUCCESS
    # share                     FAIL+DEADLOCKS
    # shareRowExclusive         SUCCESS
    # exclusive                 SUCCESS
    # accessExclusive           SUCCESS, but LOCKS against pg_dump
    
    #config
    strategy="exclusive"
    
    db=postgres
    dbuser=postgres
    queuecount=100
    concurrency=30
    
    # code
    psql84 -t -U $dbuser $db -c "create table tx_test_queue (job_id serial, status text);"
    # empty queue
    psql84 -t -U $dbuser $db -c "truncate tx_test_queue;";
    echo "Simulating 10 second pg_dump with ACCESS SHARE"
    psql84 -t -U $dbuser $db -c "lock table tx_test_queue in ACCESS SHARE mode; select pg_sleep(10); select 'pg_dump finished...'" &
    
    echo "Starting workers..."
    # queue $queuecount items
    seq $queuecount | xargs -n 1 -P $concurrency -I {} psql84 -q -U $dbuser $db -c "insert into tx_test_queue (status) values ('queued');"
    #psql84 -t -U $dbuser $db -c "select * from tx_test_queue order by job_id;"
    # process $queuecount w/concurrency of $concurrency
    case $strategy in
        "noLock")               strategySql="update tx_test_queue set status='running{}' where job_id in (select job_id from tx_test_queue where status='queued' order by job_id asc limit 1);";;
        "accessShare")          strategySql="lock table tx_test_queue in ACCESS SHARE mode; update tx_test_queue set status='running{}' where job_id in (select job_id from tx_test_queue where status='queued' order by job_id asc limit 1);";;
        "rowShare")             strategySql="lock table tx_test_queue in ROW SHARE mode; update tx_test_queue set status='running{}' where job_id in (select job_id from tx_test_queue where status='queued' order by job_id asc limit 1);";;
        "rowExclusive")         strategySql="lock table tx_test_queue in ROW EXCLUSIVE mode; update tx_test_queue set status='running{}' where job_id in (select job_id from tx_test_queue where status='queued' order by job_id asc limit 1);";;
        "shareUpdateExclusive") strategySql="lock table tx_test_queue in SHARE UPDATE EXCLUSIVE mode; update tx_test_queue set status='running{}' where job_id in (select job_id from tx_test_queue where status='queued' order by job_id asc limit 1);";;
        "share")                strategySql="lock table tx_test_queue in SHARE mode; update tx_test_queue set status='running{}' where job_id in (select job_id from tx_test_queue where status='queued' order by job_id asc limit 1);";;
        "shareRowExclusive")    strategySql="lock table tx_test_queue in SHARE ROW EXCLUSIVE mode; update tx_test_queue set status='running{}' where job_id in (select job_id from tx_test_queue where status='queued' order by job_id asc limit 1);";;
        "exclusive")            strategySql="lock table tx_test_queue in EXCLUSIVE mode; update tx_test_queue set status='running{}' where job_id in (select job_id from tx_test_queue where status='queued' order by job_id asc limit 1);";;
        "accessExclusive")      strategySql="lock table tx_test_queue in ACCESS EXCLUSIVE mode; update tx_test_queue set status='running{}' where job_id in (select job_id from tx_test_queue where status='queued' order by job_id asc limit 1);";;
        *) echo "Unknown strategy $strategy";;
    esac
    echo $strategySql
    seq $queuecount | xargs -n 1 -P $concurrency -I {} psql84 -U $dbuser $db -c "$strategySql"
    #psql84 -t -U $dbuser $db -c "select * from tx_test_queue order by job_id;"
    psql84 -U $dbuser $db -c "select count(distinct(status)) as should_output_100 from tx_test_queue;"
    psql84 -t -U $dbuser $db -c "drop table tx_test_queue;";
    

    編集する場合は、コードもここにあります:https://gist.github.com/1083936

    EXCLUSIVEモードを使用するようにアプリケーションを更新しています。これは、a)正しく、b)pg_dumpと競合しない最も制限的なモードだからです。ポストグレスロックの専門家でなくても、アプリをACCESS EXCLUSIVEから変更するという点でリスクが最も低いと思われるため、最も制限の厳しいものを選択しました。

    私は自分のテストリグと答えの背後にある一般的な考え方にかなり満足しています。これを共有することで、他の人のこの問題を解決できることを願っています。



    1. PentahoDataIntegrationおよびBAServerツールへのMySQLJDBCドライバーのインストール

    2. MYSQL自動インクリメントフィールドにJPAアノテーションを付ける方法

    3. ID列の値をプログラムで変更するにはどうすればよいですか?

    4. 11gで収集する増分統計