PostgreSQL memiliki mekanisme yang sangat nyaman untuk kunci penasehat , mereka juga kunci penasehat . Kami di Tensor menggunakannya di banyak tempat dalam sistem, tetapi hanya sedikit orang yang memahami secara detail bagaimana tepatnya mereka bekerja, dan masalah apa yang dapat diperoleh jika diperlakukan dengan buruk .
Baca lebih lanjut tentang kunci Keuntungan dari kunci rekomendasi
Perbedaan mendasar antara mekanisme ini dan kunci tingkat tabel / halaman / catatan "biasa" adalah bahwa ada beberapa fitur utama.Kunci ID Kustom
Kunci "normal" dalam PG selalu terikat ke objek database tertentu (tabel, catatan, halaman data) dan proses melayani koneksi. Kunci penasihat juga merupakan proses, tetapi alih-alih objek nyata, pengidentifikasi abstrak dapat ditetapkan sebagai (bigint) atau sebagai (integer, integer) .Ngomong-ngomong, melampirkan setiap kunci ke suatu proses berarti bahwa dengan memberi nama melalui pg_terminate_backend(pid)
atau menyelesaikan koneksi dengan benar dari sisi klien, Anda dapat menyingkirkan semua kunci yang dikenakan olehnya.CAS Periksa Penguncian Kunci
CAS adalah Compare-and-Set , yaitu verifikasi kemampuan menangkap dan penangkapan kunci itu sendiri terjadi sebagai satu operasi atom, dan jelas tidak ada yang bisa "mengganjal" di antara mereka.Artinya, jika Anda pertama kali membuat permintaan verifikasi ke pg_locks , lihat hasilnya, lalu putuskan apakah akan dikunci atau tidak, maka tidak ada yang menjamin bahwa di antara operasi ini tidak ada yang akan berhasil mengambil objek yang Anda butuhkan. Tetapi jika Anda menggunakan pg_try_advisory_lock , maka Anda akan segera menerima kunci ini, atau fungsinya hanya akan kembali FALSE
.Tanpa penangkapan tanpa pengecualian dan harapan
Kunci "normal" ada dalam model "Jika Anda sudah meminta kunci, maka tunggu. Jika Anda tidak ingin menunggu ( NOWAIT
, statement_timeout
, lock_timeout
) - inilah pengecualian " . Pendekatan ini sangat mengganggu dalam transaksi, karena Anda harus mengimplementasikan blok BEGIN-EXCEPTION-END
untuk memproses atau memutar kembali ( ROLLBACK
) transaksi.Satu-satunya cara untuk menghindari perilaku ini adalah dengan menggunakan desain SELECT ... SKIP LOCKED
yang datang dengan versi 9.5. Sayangnya, dengan metode ini, opsi "tidak memiliki apa yang harus diblokir" dan "tadinya, tetapi sudah diblokir" menjadi tidak bisa dibedakan.Kunci yang disarankan karena fungsi coba cukup kembali TRUE/FALSE
.Jangan bingung pg_advisory_lock
dan - fungsi pertama akan menunggu sampai menerima kunci, dan yang kedua hanya akan segera kembali jika tidak mungkin untuk menangkapnya "sekarang."pg_try_advisory_lock
FALSE
Mengunci di dalam dan setelah transaksi
Seperti yang saya sebutkan di atas, kunci objek "dilampirkan" ke proses dan hanya ada sebagai bagian dari transaksi saat ini di dalamnya. Bahkan hanya untuk memaksakan - tidak akan berhasil:LOCK TABLE tbl;
Dengan demikian, pada akhir transaksi, semua kunci yang dikenakan padanya dihapus. Sebaliknya, kunci penasihat pada awalnya dirancang dengan kemampuan untuk menahan kunci dan di luar ruang lingkup transaksi :SELECT pg_advisory_lock(1);
SELECT * FROM pg_locks WHERE pid = pg_backend_pid() AND locktype = 'advisory';
-[ RECORD 1 ]------+--------------
locktype | advisory
database | 263911484
relation |
page |
tuple |
virtualxid |
transactionid |
classid | 0 <-- int4 #1 int8
objid | 1 <-- int4 #2 int8
objsubid | 1
virtualtransaction | 416/475768
pid | 29264
mode | ExclusiveLock
granted | t
fastpath | f
Tetapi sudah dari versi 9.1, versi xact fungsi penasehat telah muncul yang memungkinkan Anda untuk menerapkan perilaku kunci "biasa" yang secara otomatis dirilis ketika transaksi yang memberlakukannya selesai.Contoh penggunaan di VLSI
Sebenarnya, seperti kunci lainnya, penasihat berfungsi untuk memastikan keunikan memproses sumber daya. Di negara kita, sumber daya semacam itu biasanya berupa seluruh tabel atau catatan tabel tertentu, yang karena alasan tertentu tidak ingin "dikunci".Proses monoproses pekerja
Jika kebutuhan untuk memproses beberapa data dalam basis data dipicu oleh peristiwa eksternal, tetapi multi-pemrosesan berlebihan atau dapat menyebabkan kondisi balapan , masuk akal untuk membuat hanya satu proses yang bekerja pada data ini pada satu waktu .Untuk melakukan ini, kami akan mencoba mengunci dengan pengidentifikasi tabel sebagai parameter pertama dan ID pemrosesan aplikasi spesifik sebagai yang kedua:SELECT pg_try_advisory_lock(
'processed_table'::regclass::oid
, -1
);
Jika kami kembali FALSE
, maka orang lain sudah memegang kunci seperti itu, dan secara khusus proses ini tidak perlu melakukan apa-apa, tetapi yang terbaik adalah hanya mengakhiri dengan tenang. Ini adalah bagaimana, misalnya, proses perhitungan dinamis dari biaya barang di gudang bekerja, dalam isolasi untuk setiap skema klien individu.Pemrosesan Antrian Bersamaan
Sekarang tugasnya adalah "kebalikannya" - kami ingin tugas dalam beberapa tabel antrian diproses secepat mungkin, multi-threaded, toleran terhadap kesalahan, dan bahkan dari berbagai logika bisnis (yah, kami tidak memiliki kekuatan satu) - misalnya, seperti yang dilakukan operator kami tentang transfer pelaporan elektronik ke lembaga pemerintah atau layanan OFD .Karena "pemrosesan" BL adalah server yang berbeda, tidak ada mutex yang dapat ditutup lagi. Tidak aman untuk memilih beberapa koordinator proses pendistribusian tugas khusus, "mati" dia - dan semuanya akan naik. Dan ternyata itu yang paling efisien untuk mendistribusikan tugas langsung di tingkat database, dan ada cara seperti itu - di zaman kuno, model itu secara jujur ββdimata-matai oleh Dmitry Koterov dan kemudian dimodifikasi secara kreatif.Dalam hal ini, kami mengenakan kunci pada ID tabel dan PK dari catatan tertentu:SELECT
*
FROM
queue_table
WHERE
pg_try_advisory_lock('queue_table'::regclass::oid, pk_id)
ORDER BY
pk_id
LIMIT 1;
Artinya, proses akan menerima dari tabel catatan pertama yang belum diblokir oleh saudara-saudaranya yang bersaing.Namun, jika PK tidak terdiri dari (bilangan bulat) , tetapi dari (bilangan bulat, bilangan bulat) (seperti dalam perhitungan biaya yang sama, misalnya), Anda dapat mengenakan kunci secara langsung pada pasangan ini - tidak mungkin bahwa persimpangan dengan "pesaing" akan terjadi.Penting! Jangan lupa untuk secara teratur memelihara tabel antrian Anda !Pemrosesan Dokumen Eksklusif
Ini digunakan di mana-mana dalam solusi manajemen dokumen . Memang, dalam sistem web terdistribusi, satu dan dokumen yang sama dapat dibuka untuk dilihat oleh pengguna yang berbeda secara bersamaan, tetapi hanya dapat diproses (ubah statusnya, dll.) Kapan saja hanya dengan satu.Masalah tradisional
Di mana tanpa mereka! Hampir semua bermuara pada satu hal : mereka tidak membuka apa yang mereka kunci .Multi-overlay dari satu kunci penasihat
RTFM , seperti yang mereka katakan:Jika beberapa permintaan untuk pemblokiran diterima sekaligus, mereka menumpuk, jadi jika satu sumber daya telah diblokir tiga kali, harus diblokir tiga kali agar tersedia di sesi lain.
Letakkan terlalu banyak kunci sekaligus
Ribuan dari mereka! Baca manual lagi :Baik kunci penasihat dan reguler disimpan di area memori bersama, yang ukurannya ditentukan oleh parameter konfigurasi max_locks_per_transaction
dan max_connections
. Penting agar memori ini memadai, karena jika tidak server tidak akan dapat mengeluarkan kunci apa pun . Dengan demikian, jumlah kunci yang direkomendasikan yang dapat dikeluarkan oleh server biasanya terbatas pada puluhan atau ratusan ribu, tergantung pada konfigurasi server.
Secara umum, jika suatu situasi muncul ketika Anda ingin memaksakan beberapa ribu kunci penasihat (bahkan jika Anda menghapusnya dengan benar nanti) - pikirkan dengan sangat keras di mana Anda akan menjalankan ketika server bangun.Kebocoran saat menyaring catatan
Di sini kami menerima permintaan sebelumnya dan menambahkan kondisi tidak berbahaya seperti ID pemeriksaan paritas - AND pk_id % 2 = 0
. Kedua kondisi untuk setiap entri akan diperiksa ! Akibatnya, pg_try_advisory_lock
selesai, kunci dilapiskan, dan kemudian catatan disaring berdasarkan paritas.
Atau opsi dari manual:SELECT pg_advisory_lock(id) FROM foo WHERE id = 12345;
SELECT pg_advisory_lock(id) FROM foo WHERE id > 12345 LIMIT 100;
Itu saja - pemblokiran tetap ada , tetapi kami tidak mengetahui hal ini. Diperlakukan dengan permintaan yang benar, dalam kasus terburuk - pg_advisory_unlock_all
.Oh, bingung!
Klasik genre ...Bingung dan , dan bertanya-tanya mengapa itu bekerja untuk waktu yang lama. Tetapi karena versi non-coba sedang menunggu . Bingung dan , dan bertanya-tanya ke mana kunci itu pergi - dan itu "berakhir" dengan transaksi. Dan transaksi terdiri dari satu permintaan, karena tidak ada tempat yang "secara eksplisit" dinyatakan, ya.pg_try_advisory_lock
pg_advisory_lock
pg_try_advisory_lock
pg_try_advisory_xact_lock
Bekerja melalui pgbouncer
Ini adalah sumber rasa sakit yang terpisah bagi banyak orang ketika, demi kinerja, bekerja dengan database melewati pgbouncer dalam mode transaksi .Ini berarti bahwa dua transaksi tetangga Anda yang berjalan pada koneksi yang sama ke database (yang sebenarnya melewati pgbouncer) dapat dieksekusi dalam koneksi "fisik" yang berbeda di sisi database. Dan mereka memiliki kunci sendiri ... Masing-masing memiliki
beberapa pilihan:- atau pergi bekerja melalui koneksi langsung ke database
- atau menciptakan suatu algoritma sehingga semua kunci penasehat hanya dalam transaksi (xact)
Itu saja untuk saat ini.