sql >> Databasteknik >  >> RDS >> PostgreSQL

Funktionen tar evigheter att köra för ett stort antal poster

Med största sannolikhet stöter du på tävlingsförhållanden . När du kör din funktion 1000 gånger i snabb följd i separata transaktioner , något liknande detta händer:

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
                                          ... 

Till stor del omskriven och förenklad som 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;

Relaterad fråga med mycket mer förklaring:

Förklara

  • Kör inte två separata SQL-satser. Det är dyrare och breddar tidsramen för tävlingsförhållanden. En UPDATE med en underfråga är mycket bättre.

  • Du behöver inte PL/pgSQL för den enkla uppgiften. Du kan fortfarande använd PL/pgSQL, UPDATE förblir densamma.

  • Du måste låsa den valda raden för att försvara dig mot tävlingsförhållanden. Men du kan inte göra detta med den aggregatfunktion du leder eftersom, per dokumentation :

  • Djärv betoning min. Som tur är kan du ersätta min(id) enkelt med motsvarande ORDER BY / LIMIT 1 Jag angav ovan. Kan använda ett index lika bra.

  • Om bordet är stort behöver ett index på id minst. Förutsatt att id är redan indexerad som PRIMARY KEY , det skulle hjälpa. Men detta ytterligare partiella flerkolumnindex skulle förmodligen hjälpa mycket mer :

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

Alternativa lösningar

Rådgivande lås Kan vara det överlägsna tillvägagångssättet här:

Eller så kanske du vill låsa många rader samtidigt :




  1. Finns det några bra PostgreSQL-klienter för linux?

  2. Infoga Dynamic Select Box-värde i Mysql-databasen och visa inlämnat meddelande

  3. Effektivt sätt att få @@rowcount från en fråga med hjälp av row_number

  4. Hur returnerar man rader listade i fallande ordning på COUNT(*)?