PostgreSQL
 sql >> база данни >  >> RDS >> PostgreSQL

Функцията отнема вечно да работи за голям брой записи

Най-вероятно попадате в състезателни условия . Когато стартирате функцията си 1000 пъти в бърза последователност в отделни транзакции , се случва нещо подобно:

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

До голяма степен пренаписана и опростена като 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;

Свързан въпрос с много повече обяснения:

Обяснете

  • Не изпълнявайте два отделни SQL оператора. Това е по-скъпо и разширява времевата рамка за условията на състезанието. Една UPDATE с подзаявка е много по-добре.

  • Нямате нужда от PL/pgSQL за простата задача. Все още можете използвайте PL/pgSQL, UPDATE остава същата.

  • Трябва да заключите избрания ред, за да се защитите от условията на състезание. Но не можете да направите това с агрегатната функция, която ръководите, защото по документация :

  • Удебелен акцент мой. За щастие можете да замените min(id) лесно с еквивалентния ORDER BY / LIMIT 1 Дадох по-горе. Също така може да използва индекс.

  • Ако масата е голяма, имате нужда от индекс на id поне. Ако приемем, че id вече е индексиран като PRIMARY KEY , това би помогнало. Но този допълнителен частичен многоколонен индекс вероятно ще помогне много повече :

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

Алтернативни решения

Препоръчителни ключалки Може да е най-добрият подход тук:

Или може да искате да заключите много редове наведнъж :




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. SQL заявка, използваща клауза WHERE IN

  2. Postgresql varchar брои ли се с помощта на дължина на знака в unicode или дължина на символа в ASCII?

  3. защо PG::UniqueViolation:ГРЕШКА:дублираната стойност на ключа нарушава уникалното ограничение?

  4. Как да свържете Struts 2 с Hibernate и PostgreSQL

  5. PostgreSQL и nodejs/pg, връщат вложен JSON