Dalam jawaban terkait yang Anda maksud:
- PEMBARUAN Pascagres ... BATAS 1
Tujuannya adalah untuk mengunci satu baris pada suatu waktu. Ini berfungsi baik dengan atau tanpa kunci penasehat, karena tidak ada peluang untuk kebuntuan - selama Anda tidak mencoba mengunci lebih banyak baris dalam transaksi yang sama.
Contoh Anda berbeda karena Anda ingin mengunci 3000 baris sekaligus . Ada ada potensi kebuntuan, kecuali jika semua operasi tulis bersamaan mengunci baris dalam urutan konsisten yang sama. Per dokumentasi:
Pertahanan terbaik terhadap deadlock umumnya adalah menghindarinya dengan memastikan bahwa semua aplikasi yang menggunakan database memperoleh kunci pada beberapa objek dalam urutan yang konsisten.
Terapkan itu dengan ORDER BY di subkueri Anda.
UPDATE cargo_item item
SET job_id = 'SOME_UUID', job_ts = now()
FROM (
SELECT id
FROM cargo_item
WHERE state='NEW' AND job_id is null
ORDER BY id
LIMIT 3000
FOR UPDATE
) sub
WHERE item.id = sub.id;
Ini aman dan dapat diandalkan, selama semua transaksi memperoleh kunci dalam urutan yang sama dan pembaruan bersamaan dari kolom pemesanan tidak diharapkan. (Baca kotak kuning "PERHATIAN" di akhir bab ini di manual.) Jadi ini harus aman dalam kasus Anda, karena Anda tidak akan memperbarui id
kolom.
Secara efektif hanya satu klien pada satu waktu yang dapat memanipulasi baris dengan cara ini. Transaksi bersamaan akan mencoba mengunci baris (terkunci) yang sama dan menunggu transaksi pertama selesai.
Kunci saran berguna jika Anda memiliki banyak atau sangat lama transaksi bersamaan (sepertinya Anda tidak melakukannya). Dengan hanya sedikit, secara keseluruhan akan lebih murah untuk hanya menggunakan kueri di atas dan membuat transaksi bersamaan menunggu giliran.
Semua dalam satu PEMBARUAN
Tampaknya akses bersamaan bukan masalah dalam pengaturan Anda. Konkurensi adalah masalah yang diciptakan oleh solusi Anda saat ini.
Sebaliknya, lakukan semuanya dalam satu UPDATE
. Tetapkan kumpulan n
nomor (3000 dalam contoh) untuk setiap UUID dan perbarui sekaligus. Harus tercepat.
UPDATE cargo_item c
SET job_id = u.uuid_col
, job_ts = now()
FROM (
SELECT row_number() OVER () AS rn, uuid_col
FROM uuid_tbl WHERE <some_criteria> -- or see below
) u
JOIN (
SELECT (row_number() OVER () / 3000) + 1 AS rn, item.id
FROM cargo_item
WHERE state = 'NEW' AND job_id IS NULL
FOR UPDATE -- just to be sure
) c2 USING (rn)
WHERE c2.item_id = c.item_id;
Poin utama
-
Pembagian bilangan bulat terpotong. Anda mendapatkan 1 untuk 3000 baris pertama, 2 untuk 3000 baris berikutnya. dll.
-
Saya memilih baris secara sewenang-wenang, Anda dapat menerapkan
ORDER BY
di jendela untukrow_number()
untuk menetapkan baris tertentu. -
Jika Anda tidak memiliki tabel UUID untuk dikirim (
uuid_tbl
), gunakanVALUES
ekspresi untuk memasok mereka. Contoh. -
Anda mendapatkan kumpulan 3000 baris. Batch terakhir akan kekurangan 3000 jika Anda tidak menemukan kelipatan 3000 untuk ditetapkan.