Höchstwahrscheinlich geraten Sie in Rennbedingungen . Wenn Sie Ihre Funktion 1000 Mal in schneller Folge in separaten Transaktionen ausführen , passiert so etwas:
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
...
Weitgehend umgeschrieben und vereinfacht als SQL-Funktion:
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;
Verwandte Frage mit viel mehr Erklärung:
Erklären
-
Führen Sie nicht zwei separate SQL-Anweisungen aus. Das ist teurer und erweitert den Zeitrahmen für Rennbedingungen. Ein
UPDATE
mit einer Unterabfrage ist viel besser. -
Für die einfache Aufgabe benötigen Sie kein PL/pgSQL. Sie können immer noch Verwenden Sie PL/pgSQL, das
UPDATE
bleibt gleich. -
Sie müssen die ausgewählte Reihe sperren, um sich gegen Rennbedingungen zu verteidigen. Aber Sie können dies nicht mit der von Ihnen geleiteten Aggregatfunktion tun, weil pro Dokumentation :
-
Fette Hervorhebung von mir. Glücklicherweise können Sie
min(id)
ersetzen ganz einfach mit dem entsprechendenORDER BY
/LIMIT 1
habe ich oben angegeben. Kann genauso gut einen Index verwenden. -
Wenn der Tisch groß ist, brauchen Sie ein Index auf
id
wenigstens. Angenommen, dieseid
ist bereits alsPRIMARY KEY
indiziert , das würde helfen. Aber dieser zusätzliche teilweise mehrspaltige Index würde wahrscheinlich sehr viel helfen :CREATE INDEX foo_idx ON table (id_type, id) WHERE id_used IS NULL;
Alternative Lösungen
Hinweissperren Kann hier der überlegene Ansatz sein:
Oder Sie möchten vielleicht viele Zeilen gleichzeitig sperren :