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

Jak zaznaczyć określoną liczbę wierszy w tabeli przy dostępie równoczesnym?

W powiązanej odpowiedzi, do której się odnosisz:

  • AKTUALIZACJA Postgresa... LIMIT 1

Celem jest zablokowanie jednego wiersz na raz. Działa to dobrze z blokadami doradczymi lub bez nich, ponieważ nie ma szans na zakleszczenie - o ile nie próbujesz zablokować większej liczby wierszy w tej samej transakcji.

Twój przykład różni się tym, że chcesz zablokować 3000 wierszy naraz . Jest jest możliwość zakleszczenia, z wyjątkiem sytuacji, gdy wszystkie współbieżne operacje zapisu blokują wiersze w tej samej spójnej kolejności. Zgodnie z dokumentacją:

Najlepszą ochroną przed zakleszczeniami jest generalnie unikanie ich poprzez upewnienie się, że wszystkie aplikacje korzystające z bazy danych blokują wiele obiektów w spójnej kolejności.

Zaimplementuj to za pomocą ORDER BY w podzapytaniu.

UPDATE cargo_item item
SET job_id = 'SOME_UUID', job_ts = now()
FROM  ( 
   SELECT id
   FROM   cargo_item
   WHERE  state='NEW' AND job_id is null 
   ORDER  BY id
   LIMIT  3000
   FOR UPDATE
   ) sub
WHERE  item.id = sub.id;

Jest to bezpieczne i niezawodne, o ile wszystkie transakcje uzyskują blokady w tej samej kolejności i nie należy oczekiwać współbieżnych aktualizacji kolumn porządkujących. (Przeczytaj żółte pole "UWAGA" na końcu tego rozdziału w podręczniku.) Powinno to być bezpieczne w twoim przypadku, ponieważ nie będziesz aktualizować id kolumna.

W rzeczywistości tylko jeden klient naraz może w ten sposób manipulować wierszami. Transakcje współbieżne będą próbowały zablokować te same (zablokowane) wiersze i czekać na zakończenie pierwszej transakcji.

Zamki doradcze są przydatne, jeśli masz wiele lub bardzo długo działających jednoczesnych transakcji (wygląda na to, że nie masz). Mając tylko kilka, ogólnie taniej będzie po prostu użyć powyższego zapytania i pozwolić równoczesnym transakcjom czekać na swoją kolej.

Wszystko w jednym AKTUALIZACJA

Wygląda na to, że równoczesny dostęp sam w sobie nie stanowi problemu w Twojej konfiguracji. Współbieżność to problem stworzony przez Twoje obecne rozwiązanie.

Zamiast tego zrób to wszystko w jednej UPDATE . Przypisz partie n liczby (w przykładzie 3000) do każdego identyfikatora UUID i zaktualizuj wszystkie na raz. Powinien być najszybszy.

UPDATE cargo_item c
SET    job_id = u.uuid_col
     , job_ts = now()
FROM  (
   SELECT row_number() OVER () AS rn, uuid_col
   FROM   uuid_tbl WHERE  <some_criteria>  -- or see below
   ) u
JOIN (
   SELECT (row_number() OVER () / 3000) + 1 AS rn, item.id 
   FROM   cargo_item
   WHERE  state = 'NEW' AND job_id IS NULL
   FOR    UPDATE   -- just to be sure
   ) c2 USING (rn)
WHERE  c2.item_id = c.item_id;

Główne punkty

  • Obcinanie dzielenia liczb całkowitych. Otrzymasz 1 za pierwsze 3000 wierszy, 2 za następne 3000 wierszy. itp.

  • Wybieram wiersze arbitralnie, możesz zastosować ORDER BY w oknie dla row_number() aby przypisać określone wiersze.

  • Jeśli nie masz tabeli identyfikatorów UUID do wysłania (uuid_tbl ), użyj VALUES wyrażenie, aby je dostarczyć. Przykład.

  • Otrzymasz partie 3000 rzędów. W ostatniej partii zabraknie 3000, jeśli nie znajdziesz wielokrotności 3000 do przypisania.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Tablica PHP do tablicy postgres

  2. Przewodnik po Pgpool dla PostgreSQL:część druga

  3. W mojej bazie danych PostgreSQL brakuje miejsca na dysku

  4. GROUP BY w klauzuli UPDATE FROM

  5. Alternatywny format wyjściowy dla psql