PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Kolejka zadań jako tabela SQL z wieloma konsumentami (PostgreSQL)

Używam postgresa również do kolejki FIFO. Pierwotnie używałem ACCESS EXCLUSIVE, który daje poprawne wyniki przy wysokiej współbieżności, ale ma niefortunny efekt wzajemnego wykluczania się z pg_dump, który podczas wykonywania uzyskuje blokadę ACCESS SHARE. To powoduje, że moja funkcja next() blokuje się na bardzo długi czas (czas trwania pg_dump). To było nie do przyjęcia, ponieważ jesteśmy sklepem 24x7, a klientom nie podobał się czas martwy w kolejce w środku nocy.

Pomyślałem, że musi istnieć mniej restrykcyjna blokada, która nadal byłaby bezpieczna współbieżnie i nie byłaby blokowana podczas działania pg_dump. Moje wyszukiwanie doprowadziło mnie do tego posta SO.

Potem zrobiłem kilka badań.

Poniższe tryby są wystarczające dla funkcji kolejki FIFO NEXT(), która zaktualizuje status zadania z w kolejce do biegania bez żadnej współbieżności nie powiedzie się, a także nie blokuje się przed pg_dump:

SHARE UPDATE EXCLUSIVE
SHARE ROW EXCLUSIVE
EXCLUSIVE

Zapytanie:

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;

Wynik wygląda tak:

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

Oto skrypt powłoki, który testuje wszystkie różne tryby blokady przy wysokiej współbieżności (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;";

Kod jest tutaj również, jeśli chcesz edytować:https://gist.github.com/1083936

Aktualizuję moją aplikację, aby używała trybu EXCLUSIVE, ponieważ jest to najbardziej restrykcyjny tryb, który a) jest poprawny i b) nie powoduje konfliktu z pg_dump. Wybrałem najbardziej restrykcyjny, ponieważ wydaje się najmniej ryzykowny, jeśli chodzi o zmianę aplikacji z ACCESS EXCLUSIVE bez bycia super-ekspertem w blokowaniu postgres.

Czuję się całkiem dobrze z moim stanowiskiem testowym i ogólnymi pomysłami, które kryją się za odpowiedzią. Mam nadzieję, że udostępnienie tego pomoże rozwiązać ten problem innym.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Uzyskaj liczbę dni w miesiącu w PostgreSQL

  2. Jak działa KOPIUJ i dlaczego jest o wiele szybszy niż WSTAWIANIE?

  3. Jak ZMIENIĆ tabelę PostgreSQL i uczynić kolumnę unikalną?

  4. Odejmij tygodnie od daty w PostgreSQL

  5. Zapętlaj wymiar tablicy w plpgsql