Dua seri artikel sebelumnya telah berfokus pada
isolasi dan multiversionisme dan
penjurnalan .
Dalam seri ini kita akan berbicara tentang kunci. Saya akan mematuhi istilah ini, tetapi dalam literatur mungkin ada yang lain:
kastil .
Siklus akan terdiri dari empat bagian:
- Kunci hubungan (artikel ini);
- Kunci baris ;
- Kunci benda lain dan kunci predikat;
- Kunci dalam RAM .
Materi semua artikel didasarkan pada
kursus pelatihan administratif yang dilakukan Pavel
pluzanov dan saya, tetapi jangan mengulanginya kata demi kata dan dimaksudkan untuk membaca dengan penuh pertimbangan dan eksperimen independen.

Informasi umum tentang kunci
PostgreSQL menggunakan banyak mekanisme berbeda yang digunakan untuk memblokir sesuatu (atau paling tidak disebut itu). Oleh karena itu, saya akan mulai dengan kata-kata paling umum tentang mengapa kunci diperlukan sama sekali, apa itu kunci dan bagaimana mereka berbeda satu sama lain. Kemudian kita akan melihat apa jenis ini ditemukan di PostgreSQL dan hanya setelah itu kita akan mulai menangani berbagai jenis kunci secara detail.
Kunci digunakan untuk merampingkan akses bersamaan ke sumber daya bersama.
Akses kompetitif mengacu pada akses simultan dari beberapa proses. Proses itu sendiri dapat dilakukan secara paralel (jika peralatan memungkinkan) dan secara berurutan dalam mode pembagian waktu - ini tidak penting.
Jika tidak ada persaingan, maka tidak perlu kunci (misalnya, cache buffer bersama membutuhkan kunci, tetapi yang lokal tidak).
Sebelum mengakses sumber daya, suatu proses harus mendapatkan kunci yang terkait dengan sumber daya itu. Artinya, kita berbicara tentang disiplin tertentu: semuanya bekerja selama semua proses mematuhi aturan yang ditetapkan untuk mengakses sumber daya bersama. Jika DBMS mengelola kunci, maka itu sendiri akan memantau pesanan; jika pemblokiran diatur oleh aplikasi, maka kewajiban ini menjadi tanggung jawabnya.
Pada tingkat rendah, kunci diwakili oleh bagian dari memori bersama, di mana ia dicatat dalam beberapa cara apakah kunci bebas atau ditangkap (dan, mungkin, informasi tambahan dicatat: nomor proses, waktu pengambilan, dll).
Anda mungkin memperhatikan bahwa sepotong memori bersama itu sendiri merupakan sumber daya yang memungkinkan akses kompetitif. Jika kita turun ke level yang lebih rendah, kita akan melihat bahwa primitif aksesori khusus (seperti semaphore atau mutex) yang disediakan oleh OS digunakan untuk mengatur akses. Arti mereka adalah bahwa kode yang mengakses sumber daya bersama harus dieksekusi hanya dalam satu proses pada satu waktu. Pada level terendah, primitif ini diimplementasikan berdasarkan instruksi prosesor atom (seperti test-and-set atau compare-and-swap).
Setelah sumber daya tidak lagi dibutuhkan oleh proses, ia
melepaskan kunci sehingga orang lain dapat menggunakan sumber daya.
Tentu saja, mengunci kunci tidak selalu memungkinkan: sumber daya mungkin sudah diambil oleh orang lain. Kemudian proses masuk ke antrian tunggu (jika mekanisme penguncian memberikan kesempatan ini), atau coba lagi untuk mengambil kunci setelah waktu tertentu. Dengan satu atau lain cara, ini mengarah pada fakta bahwa proses dipaksa untuk diam dalam mengantisipasi pelepasan sumber daya.
Terkadang dimungkinkan untuk menerapkan strategi non-blocking lainnya. Sebagai contoh, mekanisme multi- versi memungkinkan beberapa proses dalam beberapa kasus untuk bekerja secara bersamaan dengan versi data yang berbeda tanpa saling menghalangi.
Pada prinsipnya, sumber daya yang dilindungi dapat berupa apa saja, jika saja sumber daya ini dapat diidentifikasi dan dicocokkan secara jelas dengan alamat pemblokiran.
Sebagai contoh, sumber daya dapat menjadi objek yang bekerja dengan DBMS, seperti halaman data (diidentifikasi oleh nama file dan posisi di dalam file), tabel (oid dalam direktori sistem), baris tabel (halaman dan offset di dalam halaman). Sumber daya dapat berupa struktur dalam memori, seperti tabel hash, buffer, dll. (Diidentifikasi oleh nomor yang ditentukan sebelumnya). Kadang-kadang bahkan lebih nyaman untuk menggunakan sumber daya abstrak yang tidak memiliki makna fisik (mereka diidentifikasi hanya dengan angka unik).
Efektivitas kunci dipengaruhi oleh banyak faktor, yang mana kami bedakan dua.
- Granularity (granularity) penting jika sumber daya membentuk hierarki.
Misalnya, tabel terdiri dari halaman yang berisi baris tabel. Semua objek ini dapat bertindak sebagai sumber daya. Jika proses biasanya hanya tertarik pada beberapa baris, dan kunci diatur pada tingkat tabel, maka proses lain tidak akan dapat bekerja dengan baris yang berbeda pada saat yang sama. Oleh karena itu, semakin tinggi granularity, semakin baik untuk kemungkinan paralelisasi.
Tetapi ini mengarah pada peningkatan jumlah kunci (informasi tentang yang harus disimpan dalam memori). Dalam hal ini, peningkatan level (eskalasi) dari kunci dapat diterapkan: ketika jumlah kunci granular tingkat rendah melebihi batas tertentu, mereka diganti dengan satu kunci dari level yang lebih tinggi.
- Kunci dapat ditangkap dalam mode yang berbeda.
Nama-nama mode dapat benar-benar arbitrer, hanya matriks kompatibilitasnya satu sama lain yang penting. Mode yang tidak kompatibel dengan mode apa pun (termasuk dengan mode itu sendiri) biasanya disebut eksklusif atau eksklusif. Jika mode-mode tersebut kompatibel, maka kunci dapat ditangkap oleh beberapa proses secara bersamaan; mode seperti itu disebut shared. Secara umum, semakin banyak mode yang kompatibel satu sama lain dapat dibedakan, semakin banyak peluang diciptakan untuk paralelisme.
Menurut waktu penggunaan, kunci dapat dibagi menjadi panjang dan pendek.
- Kunci jangka panjang ditangkap untuk waktu yang berpotensi lama (biasanya sampai akhir transaksi) dan paling sering terkait dengan sumber daya seperti tabel (hubungan) dan baris. PostgreSQL biasanya mengelola kunci ini secara otomatis, tetapi pengguna tetap memiliki kontrol atas proses ini.
Kunci panjang ditandai oleh sejumlah besar mode sehingga sebanyak mungkin tindakan simultan dapat dilakukan pada data. Biasanya, untuk kunci seperti itu, ada infrastruktur yang dikembangkan (misalnya, dukungan antrian tunggu dan deteksi kebuntuan) dan alat pemantauan, karena biaya pemeliharaan semua fasilitas ini masih jauh lebih rendah dibandingkan dengan biaya operasi pada data yang dilindungi.
- Kunci jangka pendek ditangkap untuk waktu singkat (dari beberapa instruksi prosesor hingga sepersekian detik) dan biasanya merujuk pada struktur data dalam memori bersama. PostgreSQL mengelola kunci semacam itu sepenuhnya secara otomatis - Anda hanya perlu tahu tentang keberadaannya.
Kunci pendek ditandai oleh mode minimum (eksklusif dan bersama) dan infrastruktur sederhana. Dalam beberapa kasus, bahkan alat pemantauan mungkin tidak tersedia.
PostgreSQL menggunakan berbagai jenis kunci.
Kunci pada level objek adalah
kunci "berat" jangka panjang. Sumber daya di sini adalah hubungan dan objek lainnya. Jika kata pemblokiran muncul dalam teks tanpa klarifikasi, maka itu menunjukkan pemblokiran "normal".
Di antara kunci jangka panjang, kunci
level baris menonjol secara terpisah. Implementasinya berbeda dari kunci jangka panjang lainnya karena jumlahnya yang berpotensi besar (bayangkan memperbarui satu juta baris dalam satu transaksi). Kunci tersebut akan dibahas pada artikel selanjutnya.
Artikel ketiga dalam seri ini akan dikhususkan untuk kunci yang tersisa di tingkat objek, serta
kunci predikat (karena informasi tentang semua kunci ini disimpan dalam RAM dengan cara yang sama).
Kunci pendek mencakup berbagai
kunci struktur RAM . Kami akan mempertimbangkannya di artikel terakhir siklus ini.
Kunci objek
Jadi, kita mulai dengan kunci level objek. Di sini, suatu objek dipahami pada awalnya sebagai
hubungan , yaitu, tabel, indeks, sekuens, representasi terwujud, tetapi juga beberapa entitas lainnya. Kunci ini biasanya melindungi objek dari perubahan pada saat yang sama atau dari digunakan saat objek berubah, tetapi juga untuk kebutuhan lainnya.
Kata-kata buram? Ya, karena kunci dari grup ini digunakan untuk berbagai keperluan. Yang menyatukan mereka adalah bagaimana mereka diatur.
Perangkat
Kunci objek terletak di memori bersama server. Jumlah mereka dibatasi oleh produk dari nilai dua parameter:
max_locks_per_transaction ×
max_connections .
Kumpulan kunci adalah umum untuk semua transaksi, yaitu, satu transaksi dapat menangkap lebih banyak kunci daripada
max_locks_per_transaction : hanya penting bahwa jumlah total kunci dalam sistem tidak melebihi batas yang ditetapkan. Kolam dibuat saat startup, jadi mengubah salah satu dari dua opsi yang ditunjukkan memerlukan server reboot.
Semua kunci dapat dilihat dalam tampilan pg_locks.
Jika sumber daya sudah terkunci dalam mode yang tidak kompatibel, transaksi yang mencoba untuk menangkap sumber daya ini antri dan menunggu kunci untuk dirilis. Transaksi yang tertunda tidak menggunakan sumber daya prosesor: proses layanan yang sesuai “tertidur” dan terbangun oleh sistem operasi ketika sumber daya dirilis.
Kebuntuan atau situasi
kebuntuan dimungkinkan di mana satu transaksi membutuhkan sumber daya yang ditempati oleh transaksi kedua untuk melanjutkan, dan yang kedua membutuhkan sumber daya yang ditempati oleh yang pertama (dalam kasus umum, kebuntuan dan lebih dari dua transaksi dapat terjadi). Dalam hal ini, penantian akan berlanjut tanpa batas waktu, sehingga PostgreSQL secara otomatis mendeteksi situasi seperti itu dan membatalkan salah satu transaksi sehingga orang lain dapat terus bekerja. (Kita akan berbicara lebih banyak tentang kebuntuan di artikel selanjutnya.)
Jenis objek
Berikut adalah daftar jenis kunci (atau, jika Anda suka, jenis objek) yang akan kita bahas dalam artikel ini dan selanjutnya. Nama-nama diberikan sesuai dengan kolom jenis kunci pada tampilan pg_locks.
- hubungan
Kunci hubungan.
- transactionid dan virtualxid
Memblokir nomor transaksi (nyata atau virtual). Setiap transaksi itu sendiri memegang kunci eksklusif dari nomornya sendiri, sehingga kunci tersebut nyaman digunakan ketika Anda harus menunggu sampai akhir transaksi lain.
- tuple
Kunci versi string. Dalam beberapa kasus digunakan untuk menetapkan prioritas di antara beberapa transaksi yang berharap untuk mengunci baris yang sama.
Kami akan menunda diskusi tentang jenis-jenis kunci yang tersisa sampai artikel ketiga dalam siklus. Semuanya ditangkap hanya dalam mode luar biasa, atau eksklusif dan dibagikan.
- memperpanjang
Digunakan saat menambahkan halaman ke file hubungan apa pun.
- objek
Mengunci objek yang bukan hubungan (database, skema, langganan, dll.).
- halaman
Kunci halaman jarang digunakan dan hanya oleh beberapa jenis indeks.
- penasihat
Pemblokiran yang disarankan, ditetapkan secara manual oleh pengguna.
Kunci hubungan
Agar tidak kehilangan konteks, saya akan menandai pada gambar seperti itu jenis kunci, yang akan dibahas nanti.

Mode
Jika bukan yang paling penting, maka pasti yang paling "bercabang" memblokir - memblokir hubungan. Baginya, sebanyak 8 mode yang berbeda didefinisikan. Kuantitas seperti itu diperlukan agar jumlah instruksi terbesar yang dimiliki oleh satu tabel dapat dieksekusi secara bersamaan.
Tidak masuk akal untuk mempelajari mode-mode ini dengan hati atau mencoba memahami arti nama-nama mereka; yang terpenting adalah memiliki
matriks di depan mata Anda pada waktu yang tepat, yang menunjukkan mana yang saling bertentangan. Untuk kenyamanan, itu direproduksi di sini bersama dengan contoh-contoh perintah yang membutuhkan tingkat penguncian yang sesuai:
Beberapa komentar:
- 4 mode pertama memungkinkan perubahan data secara bersamaan dalam tabel, dan 4 mode selanjutnya tidak.
- Mode pertama (Access Share) adalah yang terlemah, kompatibel dengan yang lain dari yang terakhir (Access Exclusive). Mode terakhir ini eksklusif, tidak kompatibel dengan mode apa pun.
- Perintah ALTER TABLE memiliki banyak opsi, yang berbeda membutuhkan tingkat penguncian yang berbeda. Oleh karena itu, dalam matriks, perintah ini muncul pada garis yang berbeda dan ditandai dengan tanda bintang.
Misalnya, misalnya
berikan contoh. Apa yang terjadi jika saya menjalankan perintah CREATE INDEX?
Kami menemukan dalam dokumentasi bahwa perintah ini mengatur kunci dalam mode Berbagi. Menurut matriks, kami menentukan bahwa perintah itu kompatibel dengan dirinya sendiri (yaitu, Anda dapat secara bersamaan membuat beberapa indeks) dan dengan membaca perintah. Dengan demikian, perintah SELECT akan terus bekerja, tetapi perintah UPDATE, DELETE, INSERT akan diblokir.
Dan sebaliknya - transaksi tidak lengkap yang mengubah data dalam tabel akan memblokir operasi perintah CREATE INDEX. Oleh karena itu, ada varian dari perintah - CREATE INDEX CONCURRENTLY. Ini bekerja lebih lama (dan bahkan mungkin jatuh dengan kesalahan), tetapi memungkinkan untuk perubahan data secara bersamaan.
Ini bisa dilihat dalam praktik. Untuk percobaan, kami akan menggunakan tabel akun "bank" yang familier dari
siklus pertama , di mana kami akan menyimpan nomor dan jumlah akun.
=> CREATE TABLE accounts( acc_no integer PRIMARY KEY, amount numeric ); => INSERT INTO accounts VALUES (1,1000.00), (2,2000.00), (3,3000.00);
Di sesi kedua, mulai transaksi. Kami membutuhkan nomor proses layanan.
| => SELECT pg_backend_pid();
| pg_backend_pid | ---------------- | 4746 | (1 row)
Kunci apa yang dimiliki oleh transaksi yang baru dimulai? Kami mencari di pg_locks:
=> SELECT locktype, relation::REGCLASS, virtualxid AS virtxid, transactionid AS xid, mode, granted FROM pg_locks WHERE pid = 4746;
locktype | relation | virtxid | xid | mode | granted ------------+----------+---------+-----+---------------+--------- virtualxid | | 5/15 | | ExclusiveLock | t (1 row)
Seperti yang sudah saya katakan, transaksi selalu memegang kunci eksklusif (ExclusiveLock) dari nomornya sendiri, dalam hal ini, yang virtual. Tidak ada kunci lain pada proses ini.
Sekarang perbarui baris tabel. Bagaimana situasinya akan berubah?
| => UPDATE accounts SET amount = amount + 100 WHERE acc_no = 1;
=> \g
locktype | relation | virtxid | xid | mode | granted ---------------+---------------+---------+--------+------------------+--------- relation | accounts_pkey | | | RowExclusiveLock | t relation | accounts | | | RowExclusiveLock | t virtualxid | | 5/15 | | ExclusiveLock | t transactionid | | | 529404 | ExclusiveLock | t (4 rows)
Sekarang ada kunci pada tabel dan indeks yang dapat diubah (dibuat untuk kunci utama), yang digunakan oleh perintah UPDATE. Kedua kunci diambil dalam mode RowExclusiveLock. Selain itu, pemblokiran eksklusif nomor transaksi nyata telah ditambahkan (yang muncul segera setelah transaksi mulai mengubah data).
Sekarang di sesi lain kami akan mencoba membuat indeks di atas meja.
|| => SELECT pg_backend_pid();
|| pg_backend_pid || ---------------- || 4782 || (1 row)
|| => CREATE INDEX ON accounts(acc_no);
Perintah membeku untuk mengantisipasi pelepasan sumber daya. Kunci apa yang dia coba tangkap? Periksa:
=> SELECT locktype, relation::REGCLASS, virtualxid AS virtxid, transactionid AS xid, mode, granted FROM pg_locks WHERE pid = 4782;
locktype | relation | virtxid | xid | mode | granted ------------+----------+---------+-----+---------------+--------- virtualxid | | 6/15 | | ExclusiveLock | t relation | accounts | | | ShareLock | f (2 rows)
Kami melihat bahwa transaksi sedang mencoba untuk mendapatkan kunci tabel dalam mode ShareLock, tetapi tidak bisa (diberikan = f).
Lebih mudah untuk menemukan jumlah proses pemblokiran, dan secara umum beberapa angka, menggunakan fungsi yang muncul di versi 9.6 (sebelum itu saya harus menarik kesimpulan dengan melihat semua isi pg_locks dengan hati-hati):
=> SELECT pg_blocking_pids(4782);
pg_blocking_pids ------------------ {4746} (1 row)
Dan kemudian, untuk memahami situasinya, Anda dapat memperoleh informasi tentang sesi-sesi tersebut, yang mencakup angka-angka yang ditemukan:
=> SELECT * FROM pg_stat_activity WHERE pid = ANY(pg_blocking_pids(4782)) \gx
-[ RECORD 1 ]----+------------------------------------------------------------ datid | 16386 datname | test pid | 4746 usesysid | 16384 usename | student application_name | psql client_addr | client_hostname | client_port | -1 backend_start | 2019-08-07 15:02:53.811842+03 xact_start | 2019-08-07 15:02:54.090672+03 query_start | 2019-08-07 15:02:54.10621+03 state_change | 2019-08-07 15:02:54.106965+03 wait_event_type | Client wait_event | ClientRead state | idle in transaction backend_xid | 529404 backend_xmin | query | UPDATE accounts SET amount = amount + 100 WHERE acc_no = 1; backend_type | client backend
Setelah transaksi selesai, kunci dilepaskan dan indeks dibuat.
| => COMMIT;
| COMMIT
|| CREATE INDEX
Dalam antrian! ..
Untuk dapat membayangkan dengan lebih baik bagaimana tampilan kunci yang tidak kompatibel mengarah, kita akan melihat apa yang terjadi jika perintah VACUUM FULL dieksekusi selama operasi sistem.
Biarkan perintah SELECT dijalankan terlebih dahulu di tabel kami. Dia mendapat kunci pada tingkat Access Share yang paling lemah. Untuk mengontrol waktu rilis kunci, kami menjalankan perintah ini di dalam transaksi - sampai transaksi berakhir, kunci tidak akan dirilis. Pada kenyataannya, beberapa perintah dapat membaca (dan memodifikasi) tabel, dan beberapa pertanyaan bisa memakan waktu cukup lama.
=> BEGIN; => SELECT * FROM accounts;
acc_no | amount --------+--------- 2 | 2000.00 3 | 3000.00 1 | 1100.00 (3 rows)
=> SELECT locktype, mode, granted, pid, pg_blocking_pids(pid) AS wait_for FROM pg_locks WHERE relation = 'accounts'::regclass;
locktype | mode | granted | pid | wait_for ----------+-----------------+---------+------+---------- relation | AccessShareLock | t | 4710 | {} (1 row)
Kemudian administrator mengeksekusi perintah VACUUM FULL, yang membutuhkan kunci level Access Exclusive, tidak kompatibel dengan apa pun, bahkan dengan Access Share. (Perintah LOCK TABLE juga memerlukan kunci yang sama.) Antrian transaksi.
| => BEGIN; | => LOCK TABLE accounts;
=> SELECT locktype, mode, granted, pid, pg_blocking_pids(pid) AS wait_for FROM pg_locks WHERE relation = 'accounts'::regclass;
locktype | mode | granted | pid | wait_for ----------+---------------------+---------+------+---------- relation | AccessShareLock | t | 4710 | {} relation | AccessExclusiveLock | f | 4746 | {4710} (2 rows)
Tetapi aplikasi terus mengeluarkan permintaan, dan sekarang perintah SELECT muncul di sistem. Secara teoritis, dia bisa saja “tergelincir” ketika VACUUM FULL sedang menunggu, tetapi tidak - dia dengan jujur mengambil tempat dalam antrian untuk VACUUM FULL.
|| => SELECT * FROM accounts;
=> SELECT locktype, mode, granted, pid, pg_blocking_pids(pid) AS wait_for FROM pg_locks WHERE relation = 'accounts'::regclass;
locktype | mode | granted | pid | wait_for ----------+---------------------+---------+------+---------- relation | AccessShareLock | t | 4710 | {} relation | AccessExclusiveLock | f | 4746 | {4710} relation | AccessShareLock | f | 4782 | {4746} (3 rows)
Setelah transaksi pertama dengan perintah SELECT menyelesaikan dan melepaskan kunci, perintah VACUUM FULL dimulai (yang kami disimulasikan dengan perintah LOCK TABLE).
=> COMMIT;
COMMIT
| LOCK TABLE
=> SELECT locktype, mode, granted, pid, pg_blocking_pids(pid) AS wait_for FROM pg_locks WHERE relation = 'accounts'::regclass;
locktype | mode | granted | pid | wait_for ----------+---------------------+---------+------+---------- relation | AccessExclusiveLock | t | 4746 | {} relation | AccessShareLock | f | 4782 | {4746} (2 rows)
Dan hanya setelah VACUUM FULL menyelesaikan pekerjaannya dan menghilangkan kunci, semua perintah yang terakumulasi dalam antrian (PILIH dalam contoh kami) akan dapat menangkap kunci yang sesuai (Access Share) dan mengeksekusi.
| => COMMIT;
| COMMIT
|| acc_no | amount || --------+--------- || 2 | 2000.00 || 3 | 3000.00 || 1 | 1100.00 || (3 rows)
Dengan demikian, perintah yang tidak akurat dapat melumpuhkan operasi sistem untuk waktu yang secara signifikan lebih lama dari waktu pelaksanaan perintah itu sendiri.
Alat pemantauan
Tentu saja, kunci diperlukan untuk operasi yang benar, tetapi dapat menyebabkan harapan yang tidak diinginkan. Harapan tersebut dapat dipantau untuk memahami penyebabnya dan, jika mungkin, menghilangkannya (misalnya, dengan mengubah algoritma aplikasi).
Kami telah berkenalan dengan satu metode: pada saat kunci panjang, kami dapat mengeksekusi permintaan ke tampilan pg_locks, melihat transaksi yang dapat dikunci dan memblokir (fungsi pg_blocking_pids) dan mendekripsi mereka menggunakan pg_stat_activity.
Cara lain adalah dengan mengaktifkan parameter
log_lock_waits . Dalam hal ini, informasi akan muncul di log pesan server jika transaksi telah menunggu lebih lama dari
deadlock_timeout (terlepas dari kenyataan bahwa parameter untuk deadlock digunakan, kita berbicara tentang harapan normal).
Ayo kita coba.
=> ALTER SYSTEM SET log_lock_waits = on; => SELECT pg_reload_conf();
Nilai parameter
deadlock_timeout default adalah satu detik:
=> SHOW deadlock_timeout;
deadlock_timeout ------------------ 1s (1 row)
Mainkan kuncinya.
=> BEGIN; => UPDATE accounts SET amount = amount - 100.00 WHERE acc_no = 1;
UPDATE 1
| => BEGIN; | => UPDATE accounts SET amount = amount + 100.00 WHERE acc_no = 1;
Perintah UPDATE kedua mengharapkan kunci. Tunggu sebentar dan selesaikan transaksi pertama.
=> SELECT pg_sleep(1); => COMMIT;
COMMIT
Sekarang transaksi kedua dapat diselesaikan.
| UPDATE 1
| => COMMIT;
| COMMIT
Dan semua informasi penting masuk ke jurnal:
postgres$ tail -n 7 /var/log/postgresql/postgresql-11-main.log
2019-08-07 15:26:30.827 MSK [5898] student@test LOG: process 5898 still waiting for ShareLock on transaction 529427 after 1000.186 ms 2019-08-07 15:26:30.827 MSK [5898] student@test DETAIL: Process holding the lock: 5862. Wait queue: 5898. 2019-08-07 15:26:30.827 MSK [5898] student@test CONTEXT: while updating tuple (0,4) in relation "accounts" 2019-08-07 15:26:30.827 MSK [5898] student@test STATEMENT: UPDATE accounts SET amount = amount + 100.00 WHERE acc_no = 1;
2019-08-07 15:26:30.836 MSK [5898] student@test LOG: process 5898 acquired ShareLock on transaction 529427 after 1009.536 ms 2019-08-07 15:26:30.836 MSK [5898] student@test CONTEXT: while updating tuple (0,4) in relation "accounts" 2019-08-07 15:26:30.836 MSK [5898] student@test STATEMENT: UPDATE accounts SET amount = amount + 100.00 WHERE acc_no = 1;
Untuk dilanjutkan .