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

Funkcja, która działa w nieskończoność dla dużej liczby rekordów

Najprawdopodobniej napotykasz warunki wyścigowe . Gdy uruchomisz swoją funkcję 1000 razy w krótkim odstępie czasu w osobnych transakcjach , dzieje się coś takiego:

T1            T2            T3            ...
SELECT max(id) -- id 1
              SELECT max(id)  -- id 1
                            SELECT max(id)  -- id 1
                                          ...
              Row id 1 locked, wait ...
                            Row id 1 locked, wait ...
UPDATE id 1
                                          ... 

COMMIT
              Wake up, UPDATE id 1 again!
              COMMIT
                            Wake up, UPDATE id 1 again!
                            COMMIT
                                          ... 

W dużej mierze przepisany i uproszczony jako funkcja SQL:

CREATE OR REPLACE FUNCTION get_result(val1 text, val2 text)
  RETURNS text AS 
$func$
   UPDATE table t
   SET    id_used = 'Y'
        , col1 = val1
        , id_used_date = now() 
   FROM  (
      SELECT id
      FROM   table 
      WHERE  id_used IS NULL
      AND    id_type = val2
      ORDER  BY id
      LIMIT  1
      FOR    UPDATE   -- lock to avoid race condition! see below ...
      ) t1
   WHERE  t.id_type = val2
   -- AND    t.id_used IS NULL -- repeat condition (not if row is locked)
   AND    t.id = t1.id
   RETURNING  id;
$func$  LANGUAGE sql;

Powiązane pytanie z dużo większym wyjaśnieniem:

Wyjaśnij

  • Nie uruchamiaj dwóch oddzielnych instrukcji SQL. To jest droższe i wydłuża ramy czasowe w warunkach wyścigu. Jedna UPDATE z podzapytanie jest znacznie lepsze.

  • Nie potrzebujesz PL/pgSQL do prostego zadania. Nadal możesz użyj PL/pgSQL, UPDATE pozostaje bez zmian.

  • Musisz zablokować wybrany rząd, aby obronić się przed warunkami wyścigu. Ale nie możesz tego zrobić za pomocą funkcji agregującej, którą kierujesz, ponieważ na dokumentację :

  • Moje odważne podkreślenie. Na szczęście możesz zastąpić min(id) łatwo z równoważnym ORDER BY / LIMIT 1 Podałem powyżej. Równie dobrze może używać indeksu.

  • Jeśli stół jest duży, potrzebujesz indeks na id przynajmniej. Zakładając, że id jest już zaindeksowany jako PRIMARY KEY , to by pomogło. Ale ten dodatkowy częściowy indeks wielokolumnowy prawdopodobnie pomogłoby o wiele więcej :

    CREATE INDEX foo_idx ON table (id_type, id)
    WHERE id_used IS NULL;
    

Alternatywne rozwiązania

Zamki doradcze Może to być lepsze podejście tutaj:

Możesz też zablokować wiele wierszy naraz :




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Grupuj wartości SQL dla jednej kolumny według innej kolumny

  2. Policz wiersze po dołączeniu do trzech tabel w PostgreSQL

  3. Korzystanie z funkcji okna OVER w SQLAlchemy

  4. Jak monitorować PostgreSQL za pomocą Nagios

  5. Eksportuj dane tabeli Postgresql za pomocą pgAdmin