Lompat ke konten

Telusuri kegagalannya langkah demi langkah

Tekan Langkah berikutnya untuk maju satu pernyataan. Skor menunjukkan siapa melakukan apa dan kapan; panel di bawahnya menunjukkan apa yang dipegang mesin pada saat itu juga. Ganti mesin atau isolation level-nya dan langkah yang sama langsung dijalankan ulang.

Yang dijalankan

01234567T1committedT2committedb1b2r1[P:1..2]{1,2}r2[P:1..2]{1,2}w1[1=0]w2[2=0]c1c2anomali
Key
1 Dr. A
2 Dr. B
Nilai
0 berhenti berjaga
1 sedang berjaga

Step 0, T1: begin.

Telusuri langkahnyaLangkah 0 dari 7

Belum ada yang salah

PostgreSQL 16 · READ COMMITTED

Sampai langkah ini belum ada satu pun definisi terbitan yang terpenuhi. Lanjutkan melangkah — sebuah anomali menjadi benar pada satu momen tertentu, dan tandanya akan muncul di langkah tempat itu terjadi.

Bagaimana cara membacanya?

Cara membaca partitur

Tiap garis mendatar adalah satu transaksi — satu session basis data yang mengirim statement-nya dari kiri ke kanan. Segala sesuatu pada kolom tegak yang sama terjadi pada titik yang sama dalam eksekusi, jadi membaca lurus ke bawah memberi tahu Anda apa yang sedang kedua session lakukan terhadap satu sama lain.

Satu garis per transaksi
Tiap transaksi punya warna dan bentuk penanda sendiri, jadi keduanya tidak pernah hanya dibedakan lewat warna.
Penanda kosong — sebuah pembacaan
Nilai yang dilihatnya dicetak di atas penanda. Nilai itulah inti cerita pada hampir semua anomali.
Penanda terisi — sebuah penulisan
Insert, update, atau delete. Baris yang disentuhnya disebut di bawahnya.
Satu garis tegak — commit
Transaksi selesai dan pekerjaannya menjadi permanen serta terlihat oleh semua orang.
Dua garis tegak — rollback
Transaksi dibatalkan, entah atas permintaannya sendiri atau karena dimatikan oleh mesin.
Busur putus-putus — sebuah penungguan
Statement tidak bisa lanjut dan terhalang oleh lock. Ujung busurnya jatuh pada langkah yang akhirnya melepaskannya.
Kurung di bawah paranada — berapa lama pandangan dibekukan
Rentang snapshot transaksi itu: diambil sekali, pada langkah tempat ia dimulai, lalu dipakai untuk semua pembacaan sampai transaksinya berakhir. Level yang mengambil snapshot baru pada tiap statement tidak punya rentang untuk digambar, dan justru itulah perbedaan REPEATABLE READ dan READ COMMITTED dalam satu gambar.
Lingkaran putus-putus pada kurung — commit yang tak terlihat
Transaksi lain commit di dalam rentang Anda. Ia berhasil dan datanya sudah ada di tabel, dan transaksi ini tetap membaca versi yang lama — bukan bug, melainkan janji yang dibuat level tersebut.
Garis tegas dengan segitiga — posisi Anda
Langkah yang sedang ditampilkan pada panel di bawah. Menelusuri langkah menggeserkannya.
Kurung merah — saat semuanya menjadi salah
Letaknya di atas langkah ketika anomali menjadi tak terhindarkan. Merah tidak dipakai untuk hal lain di situs ini.
Labelnya, misalnya w1[x]
Notasi baku untuk jadwal: r berarti baca, w tulis, c commit, a abort. Angkanya adalah transaksinya, huruf dalam kurung siku adalah barisnya.

Geser sebuah tanda ke samping untuk mengubah urutan sisipan lalu menjalankannya ulang. Sebuah tanda hanya bisa bergerak di antara operasi tetangganya dalam transaksi yang sama — sebuah session mengirim statement-nya secara berurutan, jadi yang bisa Anda pilih hanyalah cara menyisipkannya. Tombol panah kiri dan kanan juga bisa dipakai.

Isi mesin pada langkah ini

Keadaan basis data pada statement di atas — bukan pada akhir eksekusi. Majukan dan mundurkan langkahnya untuk melihat semua ini berubah.

Rantai versi

Setiap penulisan membuat versi baru, bukan menggantikan yang lama. xmin adalah transaksi yang membuatnya, xmax transaksi yang menggantikan atau menghapusnya.

Aturannya: Anda melihat sebuah versi jika transaksi pada xmin sudah commit ketika snapshot Anda diambil, dan xmax kosong atau berisi transaksi yang belum commit saat itu. Telusuri versi dari yang terbaru dan ambil yang pertama lolos.

Aturannya, dalam kata-kata vendor
When a transaction uses this isolation level, a SELECT query (without a FOR UPDATE/SHARE clause) sees only data committed before the query began; it never sees either uncommitted data or changes committed by concurrent transactions during the query's execution. In effect, a SELECT query sees a snapshot of the database as of the instant the query begins to run.
Key1
NilaixminxmaxStatus versi ini
1initialhidup
Key2
NilaixminxmaxStatus versi ini
1initialhidup

Lock yang dipegang

Record lock mengunci sebuah baris. Gap lock mengunci ruang antar baris, dan gunanya mencegah sebuah insert muncul di tempat yang sudah dilihat pembaca.

Dua transaksi tidak bisa memegang lock yang berbenturan pada baris yang sama, jadi statement kedua menunggu sampai transaksi pertama berakhir. Menunggu bukan error: statement itu selesai belakangan, terhadap baris apa pun yang sudah jadi saat itu.

Aturannya, dalam kata-kata vendor
In this case, the would-be updater will wait for the first updating transaction to commit or roll back (if it is still in progress). If the first updater rolls back, then its effects are negated and the second updater can proceed with updating the originally found row. If the first updater commits, the second updater will ignore the row if the first updater deleted it, otherwise it will attempt to apply its operation to the updated version of the row.

Tidak ada lock yang dipegang pada langkah ini.

Snapshot

Transaksi mana saja yang sudah commit ketika tiap snapshot diambil.

Belum ada snapshot yang diambil.

Tabel yang sudah commit

1=1 2=1

Semua urutan penyisipan statement ini

Setiap session mengirim statement-nya sendiri secara berurutan, jadi satu-satunya kebebasan adalah bagaimana keduanya disisipkan — total 70 urutan. Semuanya dijalankan pada setiap level mesin ini.

  • READ UNCOMMITTED36 / 70write-skew
  • READ COMMITTED36 / 70write-skew
  • REPEATABLE READ36 / 70write-skew
  • SNAPSHOTlevel tidak ada
  • SERIALIZABLE0 / 70tidak ada anomali pada urutan mana pun36 malah di-abort

Ini adalah cacah urutan, bukan peluang. Urutan penyisipan di dunia nyata tidak terdistribusi merata, dan tidak ada satu pun di sini yang menyatakan seberapa sering sesuatu terjadi di bawah beban — hanya apa yang mungkin terjadi sama sekali.

Apa yang dilakukan mesin sungguhan

Statement yang persis sama ini dijalankan terhadap mesin ini di dalam container, lalu apa yang dilakukannya dicatat. Bukan pemeragaan ulang — rekaman di bawah adalah bukti yang harus dicocoki oleh model.

16.14direkam 2026-08-06postgres:16-alpine

Statement yang persis sama ini dijalankan terhadap mesin ini di dalam container, lalu apa yang dilakukannya dicatat. Bukan pemeragaan ulang — rekaman di bawah adalah bukti yang harus dicocoki oleh model.
LangkahMesin mengembalikanModel ini menyatakan
0 b1okok
1 b2okok
2 r1[P:1..2]{1=1, 2=1}{1=1, 2=1}
3 r2[P:1..2]{1=1, 2=1}{1=1, 2=1}
4 w1[1=0]okok
5 w2[2=0]okok
6 c1okok
7 c2okok

Kedua kolom itu sepakat karena build-nya gagal kalau tidak — pnpm test:oracle membandingkan setiap eksekusi yang direkam dengan model, kolom demi kolom, dan ketidaksepakatan adalah kesalahan model, tidak pernah kesalahan rekaman.

Schedule yang sama pada level lainnya

Setiap level pada mesin ini, dijalankan atas statement yang persis sama, beserta langkah pertama yang membuatnya tidak lagi sama dengan eksekusi di atas. Pilih salah satu untuk berpindah ke sana.

Tidak conflict-serializable

T1 must run before T2, and T2 must run before T1 — which is impossible, so no order of these transactions one after another produces this outcome.

Jadwal jaga yang menjadi kosong

Situasinya: Dua dokter sedang berjaga dan setidaknya satu harus tetap berjaga. Masing-masing membuka jadwal, melihat bahwa yang lain berjaga, lalu mengeluarkan dirinya. Keduanya commit.

Pelajarannya: Inilah write skew: anomali yang tidak ada dalam daftar ANSI. REPEATABLE READ pada PostgreSQL adalah snapshot isolation, dan mengizinkannya: kedua transaksi commit dan tidak ada yang berjaga. Hanya SERIALIZABLE yang menangkapnya, dan caranya dengan membatalkan transaksi kedua yang commit karena read/write dependency — bukan dengan memblokir. MySQL InnoDB juga mengizinkannya di REPEATABLE READ, dan di SERIALIZABLE ia tidak mendeteksi apa pun — ia deadlock. SQL Server mengizinkannya di SNAPSHOT. Dan Oracle mengakhiri perdebatan: ia mengizinkannya di level yang bernama SERIALIZABLE, karena SERIALIZABLE pada Oracle adalah snapshot isolation.