sql >> Databasteknik >  >> RDS >> PostgreSQL

Jobbkö som SQL-tabell med flera konsumenter (PostgreSQL)

Jag använder postgres för en FIFO-kö också. Jag använde ursprungligen ACCESS EXCLUSIVE, som ger korrekta resultat i hög samtidighet, men har den olyckliga effekten att vara ömsesidigt uteslutande med pg_dump, som får ett ACCESS SHARE-lås under dess exekvering. Detta gör att min next()-funktion låses under mycket lång tid (längden på pg_dump). Detta var inte acceptabelt eftersom vi är en 24x7-butik och kunderna inte gillade dödtiden i kön mitt i natten.

Jag tänkte att det måste finnas ett mindre restriktivt lås som fortfarande skulle vara samtidigt säkert och inte låst medan pg_dump körs. Min sökning ledde mig till detta SO-inlägg.

Sedan gjorde jag lite research.

Följande lägen är tillräckliga för en FIFO-kö NEXT()-funktion som uppdaterar statusen för ett jobb från köad till kör utan någon samtidighet misslyckas, och inte heller blockera mot pg_dump:

SHARE UPDATE EXCLUSIVE
SHARE ROW EXCLUSIVE
EXCLUSIVE

Fråga:

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;

Resultatet ser ut som:

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

Här är ett skalskript som testar alla olika låslägen med hög samtidighet (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;";

Koden finns här också om du vill redigera:https://gist.github.com/1083936

Jag uppdaterar min applikation för att använda EXCLUSIVE-läget eftersom det är det mest restriktiva läget som a) är korrekt och b) inte står i konflikt med pg_dump. Jag valde det mest restriktiva eftersom det verkar vara minst riskabelt när det gäller att ändra appen från ACCESS EXCLUSIVE utan att vara en uberexpert på postgres-låsning.

Jag känner mig ganska bekväm med min testrigg och med de allmänna idéerna bakom svaret. Jag hoppas att dela detta hjälper till att lösa det här problemet för andra.



  1. Hur man tar bort ett databaspostkonto från en profil i SQL Server (T-SQL)

  2. Använda MySQL-klientapplikationer

  3. Hur man använder den lagrade proceduren 'sp_server_info' i SQL Server

  4. Hur man redigerar MySQL my.cnf-filen