sql >> Database teknologi >  >> RDS >> PostgreSQL

Jobkø som SQL-tabel med flere forbrugere (PostgreSQL)

Jeg bruger også postgres til en FIFO-kø. Jeg brugte oprindeligt ACCESS EXCLUSIVE, som giver korrekte resultater i høj samtidighed, men har den uheldige effekt at være gensidigt udelukkende med pg_dump, som får en ACCESS SHARE-lås under udførelsen. Dette får min next() funktion til at låse i meget lang tid (varigheden af ​​pg_dump). Dette var ikke acceptabelt, da vi er en 24x7 butik, og kunderne ikke kunne lide den døde tid i køen midt om natten.

Jeg regnede med, at der måtte være en mindre restriktiv lås, som stadig ville være samtidig sikker og ikke låse, mens pg_dump kører. Min søgen førte mig til dette SO-indlæg.

Så lavede jeg noget research.

Følgende tilstande er tilstrækkelige til en FIFO-kø NEXT()-funktion, som opdaterer status for et job fra at løbe uden samtidig fejl, og heller ikke blokere mod pg_dump:

SHARE UPDATE EXCLUSIVE
SHARE ROW EXCLUSIVE
EXCLUSIVE

Forespørgsel:

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 sådan ud:

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

Her er et shell-script, som tester alle de forskellige låsetilstande ved høj samtidighed (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 er også her, hvis du vil redigere:https://gist.github.com/1083936

Jeg opdaterer min applikation til at bruge den EKSKLUSIVE tilstand, da det er den mest restriktive tilstand, som a) er korrekt og b) ikke er i konflikt med pg_dump. Jeg valgte det mest restriktive, da det virker mindst risikabelt i forhold til at ændre appen fra ACCESS EXCLUSIVE uden at være uber-ekspert i postgres-låsning.

Jeg føler mig ret godt tilpas med min testrigning og med de generelle ideer bag svaret. Jeg håber, at deling af dette hjælper med at løse dette problem for andre.



  1. Sådan benchmarker du PostgreSQL-ydelse ved hjælp af Sysbench

  2. MySQL Ligesom flere værdier

  3. Brug OBJECTPROPERTY() til at finde ud af, om en tabel er en systemtabel i SQL Server

  4. Genererer sql-indsæt til Oracle