sql >> Databáze >  >> RDS >> PostgreSQL

Funkce trvá věčnost, než se spustí pro velké množství záznamů

S největší pravděpodobností narazíte na závodní podmínky . Když svou funkci spustíte 1000krát rychle za sebou v samostatných transakcích , stane se něco takového:

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

Z velké části přepsáno a zjednodušeno jako funkce 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;

Související otázka s mnohem podrobnějším vysvětlením:

Vysvětlete

  • Nespouštějte dva samostatné příkazy SQL. To je dražší a prodlužuje to časový rámec pro podmínky závodu. Jedna UPDATE s poddotazem je mnohem lepší.

  • Pro jednoduchý úkol nepotřebujete PL/pgSQL. Stále můžete použijte PL/pgSQL, UPDATE zůstává stejný.

  • Chcete-li se bránit proti rasovým podmínkám, musíte vybraný řádek uzamknout. Ale nemůžete to udělat s agregační funkcí, kterou vedete, protože podle dokumentace :

  • Odvážný důraz můj. Naštěstí můžete nahradit min(id) snadno pomocí ekvivalentního ORDER BY / LIMIT 1 Poskytl jsem výše. Stejně dobře může používat index.

  • Pokud je stůl velký, potřebujete index na id alespoň. Za předpokladu, že id je již indexován jako PRIMARY KEY , to by pomohlo. Ale tento další částečný vícesloupcový index pravděpodobně pomůže mnohem více :

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

Alternativní řešení

Poradní zámky Zde může být lepší přístup:

Nebo můžete chtít uzamknout mnoho řádků najednou :




  1. Jak opravit 'java.lang.ClassNotFoundException:com.mysql.jdbc.Driver' po jeho přidání do cesty sestavení a zaregistrování pomocí Class.forName();

  2. Nejlepší metoda pro ověření víceúrovňových relačních závislostí

  3. Nepodporovaná možnost konfigurace pro services.db:'images'

  4. Požadavek na výběr MySQL paralelně (python)