Interaktif · Berjalan di peramban · Tanpa daftar akun
Lihat dua transaksi diam-diam saling merusak.
Simulator basis data yang interaktif. Jalankan dua transaksi berdampingan, selangkah demi selangkah, dan lihat persis di mana basis data memberi jawaban yang salah — lalu ganti mesin atau isolation level-nya dan lihat langkah yang sama berakhir berbeda.
Membuka schedule di bawah, berjalan pada PostgreSQL 16 di REPEATABLE READ.
Satu kegagalan yang mudah dibayangkan
Aturan rumah sakit: minimal satu dokter harus tetap berjaga. Saat ini ada dua yang berjaga. Keduanya memutuskan pulang pada saat yang sama.
- Dr ADokter A memeriksa berapa dokter yang sedang berjaga. Jawabannya dua.
- Dr BDokter B memeriksa pada detik yang sama. Dua juga.
- Dr AA melihat masih ada satu dokter lain, jadi A berhenti berjaga.
- Dr BB melihat masih ada satu dokter lain, jadi B berhenti berjaga.
Tidak ada yang berjaga. Tak satu pun transaksi menyentuh baris milik yang lain, tidak ada yang terkunci, tidak ada kesalahan yang muncul, dan keduanya berhasil commit. Inilah write skew, dan sebagian besar basis data mengizinkannya pada level yang kemungkinan besar Anda pakai sekarang.
Dengan bahasa sederhana
Basis data Anda menjalankan banyak transaksi sekaligus. Agar tetap cepat, ia membiarkan tiap transaksi melihat versi pekerjaan transaksi lain yang sedikit basi atau setengah jadi. Biasanya itu tidak terasa. Kadang dua transaksi bertumpang tindih pada urutan yang pas salahnya, dan hasilnya jawaban yang memang keliru — uang terhitung dua kali, aturan yang ternyata tidak dijaga siapa pun, baris yang lenyap di tengah pembacaan. Setelan yang menentukan seberapa banyak tumpang tindih yang diizinkan itulah isolation level. Situs ini membuat Anda bisa sengaja memicu kegagalan itu dan menyaksikannya terjadi.
Satu eksekusi, dari awal sampai akhir
Ini keseluruhannya, langkah demi langkah, dengan angka yang sebenarnya — dihasilkan dengan menjalankan schedule ini melalui simulator yang sama dengan yang dipakai seluruh situs. Tidak ada yang perlu Anda tekan di sini. Perhatikan bahwa tidak ada satu pun yang gagal: setiap statement di bawah ini berhasil.
PostgreSQL 16 · REPEATABLE READ
- Key 1
- Dr. A
- Key 2
- Dr. B
- = 0
- berhenti berjaga
- = 1
- sedang berjaga
- 0b1begin
- 1b2begin
- 2r1[P:1..2]range read over keys 1..2→ {1=1, 2=1}
bagian dari tabel sebelum run dimulai
- 3r2[P:1..2]range read over keys 1..2→ {1=1, 2=1}
bagian dari tabel sebelum run dimulai
- 4w1[1=0]write key 1 = 0
- 5w2[2=0]write key 2 = 0
T1 wrote key 1 at step 4 and T2 wrote key 2 at step 5. Each had already read the row the other wrote, neither saw the other's write, and no row was written twice — so nothing conflicted and both committed.
- 6c1commit
- 7c2commit
Tabelnya berakhir menjadi 1=0 2=0
Tidak ada transaksi yang membaca data yang belum commit, tidak ada yang menulis baris yang ditulis transaksi lain, tidak ada lock yang berebut dan tidak ada error yang muncul. Setiap pembacaan benar per saat snapshot-nya diambil. Kedua snapshot itu diambil sebelum kedua penulisan terjadi, dan itulah seluruh bug-nya.
Delapan langkah yang sama, satu level lebih tinggi
Tidak ada yang berubah pada schedule-nya — statement yang sama, urutan yang sama, data yang sama. Hanya isolation level-nya yang berbeda, dan eksekusi di bawah ini dihasilkan dengan cara yang sama seperti yang di atas.
PostgreSQL 16 · SERIALIZABLE
- 0b1
- 1b2
- 2r1[P:1..2]
- 3r2[P:1..2]
- 4w1[1=0]
- 5w2[2=0]
- 6c1
- 7c240001 could not serialize access due to read/write dependencies among transactions
Tabelnya berakhir menjadi 1=0 2=1
Satu dokter tetap berjaga, karena basis data menolak commit yang kedua alih-alih mengizinkannya.
Cara memakainya
- 1
Pilih satu kegagalan
Tiap skenario adalah kegagalan klasik yang nyata, ditulis sebagai dua atau tiga transaksi dengan statement-nya disisipkan dalam urutan tertentu.
- 2
Telusuri langkah demi langkah
Tekan Berikutnya untuk maju satu statement. Partitur menunjukkan siapa melakukan apa dan kapan; panel di bawahnya menunjukkan keadaan mesin pada saat itu juga.
- 3
Ubah satu hal
Ganti mesinnya atau naikkan isolation level-nya, dan langkah yang sama langsung dijalankan ulang. Apa yang berubah — dan apa yang keras kepala tidak berubah — itulah seluruh pelajarannya.
Write skew adalah intinya
Dua transaksi membaca data yang sama, masing-masing memverifikasi sebuah constraint, masing-masing menulis baris yang berbeda, dan keduanya commit. Tidak ada baris yang sama, tidak ada perebutan lock, tidak ada bentrokan versi — dan constraint itu dilanggar oleh gabungannya. Anomali ini tidak ada dalam daftar ANSI, diizinkan oleh snapshot isolation, dan paling mungkin merugikan aplikasi nyata.
Nama level berarti berbeda di mesin yang berbeda
REPEATABLE READ pada PostgreSQL adalah snapshot isolation: ia mencegah phantom — yang tidak diwajibkan ANSI — dan mengizinkan write skew. READ UNCOMMITTED pada PostgreSQL diam-diam berperilaku sebagai READ COMMITTED. SERIALIZABLE pada Oracle adalah snapshot isolation, jadi ia mengizinkan write skew meski namanya begitu. Tidak satu pun dari ini bisa diturunkan dari pengetahuan umum tentang MVCC.
Diuji terhadap basis data sungguhan
Setiap jadwal di sini telah dijalankan terhadap mesin sungguhan di dalam container, dan apa yang dilakukannya — nilai yang dibaca, penungguan, kode kesalahan, transaksi mana yang dibatalkan, tabel akhirnya — disimpan sebagai fixture yang menguji model ini. Bila model dan basis data berbeda, modelnya yang salah.
Kegagalan yang bisa disebut namanya di sini
Rumus di samping tiap nama adalah notasi baku untuk sebuah jadwal: r berarti baca, w berarti tulis, c berarti commit; angkanya menunjukkan transaksi mana yang melakukannya, dan huruf dalam kurung siku menunjukkan barisnya. Jadi w1[x] r2[x] dibaca “transaksi 1 menulis baris x, lalu transaksi 2 membaca baris x”.
- Dirty write
P0w1[x] ... w2[x] ... ((c1 or a1) and (c2 or a2) in any order)
tidak ada dalam daftar ANSI SQL-92
- Dirty read
P1w1[x] ... r2[x] ... (c1 or a1)
ada dalam daftar ANSI SQL-92
- Lost update
P4r1[x] ... w2[x] ... w1[x] ... c1
tidak ada dalam daftar ANSI SQL-92
- Non-repeatable read
P2r1[x] ... w2[x] ... c2 ... r1[x]
ada dalam daftar ANSI SQL-92
- Phantom read
P3r1[P] ... w2[y in P] ... c2 ... r1[P]
ada dalam daftar ANSI SQL-92
- Read skew
A5Ar1[x] ... w2[x] ... w2[y] ... c2 ... r1[y]
tidak ada dalam daftar ANSI SQL-92
- Write skew
A5Br1[x] ... r2[y] ... w1[y] ... w2[x] ... (c1 and c2)
tidak ada dalam daftar ANSI SQL-92