PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Fungsi membutuhkan waktu lama untuk dijalankan untuk sejumlah besar catatan

Kemungkinan besar Anda mengalami kondisi balapan . Saat Anda menjalankan fungsi Anda 1000 kali berturut-turut dengan cepat dalam transaksi terpisah , sesuatu seperti ini terjadi:

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

Sebagian besar ditulis ulang dan disederhanakan sebagai fungsi 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;

Pertanyaan terkait dengan lebih banyak penjelasan:

Jelaskan

  • Jangan menjalankan dua pernyataan SQL terpisah. Itu lebih mahal dan memperlebar kerangka waktu untuk kondisi balapan. Satu UPDATE dengan subquery jauh lebih baik.

  • Anda tidak perlu PL/pgSQL untuk tugas sederhana. Anda tetap bisa gunakan PL/pgSQL, UPDATE tetap sama.

  • Anda perlu mengunci baris yang dipilih untuk bertahan melawan kondisi balapan. Tetapi Anda tidak dapat melakukan ini dengan fungsi agregat yang Anda tuju karena, per dokumentasi :

  • Penekanan saya yang berani. Untungnya, Anda dapat mengganti min(id) mudah dengan ORDER BY yang setara / LIMIT 1 saya sediakan di atas. Dapat menggunakan indeks juga.

  • Jika mejanya besar, Anda perlu indeks pada id paling sedikit. Dengan asumsi bahwa id sudah diindeks sebagai PRIMARY KEY , itu akan membantu. Tetapi indeks multikolom parsial tambahan ini mungkin akan membantu lebih banyak :

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

Solusi alternatif

Kunci saran Mungkin pendekatan yang unggul di sini:

Atau Anda mungkin ingin mengunci banyak baris sekaligus :




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Pesan kesalahan SQLAlchemy yang aneh:TypeError:objek 'dict' tidak mendukung pengindeksan

  2. Cara membuat Postgres Copy mengabaikan baris pertama file txt besar

  3. Tidak dapat menginstal psycopg2 Ubuntu

  4. Perbarui setiap baris dengan datetime acak antara dua tanggal

  5. apa itu @JoinColumn dan bagaimana menggunakannya di Hibernate