sql >> Database teknologi >  >> RDS >> PostgreSQL

Funktion tager evigt at køre for et stort antal poster

Mest sandsynligt løber du ind i løbsforhold . Når du kører din funktion 1000 gange hurtigt efter hinanden i særskilte transaktioner , sker der noget som dette:

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

Stort set omskrevet og forenklet 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;

Relateret spørgsmål med meget mere forklaring:

Forklar

  • Kør ikke to separate SQL-sætninger. Det er dyrere og udvider tidsrammen for løbsforhold. Én UPDATE med en underforespørgsel er meget bedre.

  • Du behøver ikke PL/pgSQL til den simple opgave. Du kan stadig brug PL/pgSQL, UPDATE forbliver den samme.

  • Du skal låse den valgte række for at forsvare dig mod løbsforhold. Men du kan ikke gøre dette med den samlede funktion, du leder, fordi pr. dokumentation :

  • Fed fremhævelse min. Heldigvis kan du erstatte min(id) nemt med den tilsvarende ORDER BY / LIMIT 1 Jeg gav ovenfor. Kan lige så godt bruge et indeks.

  • Hvis bordet er stort, skal du bruge et indeks på id i det mindste. Forudsat at id er allerede indekseret som PRIMARY KEY , det ville hjælpe. Men dette yderligere delvise flerkolonneindeks ville nok hjælpe meget mere :

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

Alternative løsninger

Rådgivende låse Kan være den overlegne tilgang her:

Eller du vil måske låse mange rækker på én gang :




  1. Tillad brugere at bedømme en kommentar én gang PHP MySQL

  2. SQLiteException ingen sådan tabel:ItemTable under kompilering:Select_id,.... from ItemTable

  3. Hibernate @OneToMany kaster MySQLSyntaxErrorException:Du har en fejl i din SQL-syntaks

  4. PHP Multidimensional array til uordnet liste, opbygning af url-sti