Kunci penasehat yang fantastis dan di mana mereka tinggal

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-ENDuntuk memproses atau memutar kembali ( ROLLBACK) transaksi.

Satu-satunya cara untuk menghindari perilaku ini adalah dengan menggunakan desain SELECT ... SKIP LOCKEDyang 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_lockdan - fungsi pertama akan menunggu sampai menerima kunci, dan yang kedua hanya akan segera kembali jika tidak mungkin untuk menangkapnya "sekarang."pg_try_advisory_lockFALSE

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;
-- ERROR:  LOCK TABLE can only be used in transaction blocks

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 --   worker'
);

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_transactiondan 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_lockselesai, kunci dilapiskan, dan kemudian catatan disaring berdasarkan paritas.



Atau opsi dari manual:
SELECT pg_advisory_lock(id) FROM foo WHERE id = 12345; -- ok
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_lockpg_advisory_lock

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

All Articles