Lompat ke konten

Pustaka kegagalan

Setiap anomali klasik sebagai jadwal yang bisa dijalankan, dengan penggambaran yang membuat risikonya jelas dan daftar level yang mengizinkannya. Setiap strip menunjukkan level yang terdokumentasi, satu baris per mesin.

  • Mulai di siniWrite skewA5B · tidak ada dalam daftar ANSI SQL-92

    Jadwal jaga yang menjadi kosong

    write-skew

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

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: Diizinkan padaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: Diizinkan padaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: Diizinkan padaSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: Diizinkan padaSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: Diizinkan padaREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: Diizinkan pada
    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.

    Buka di lab
  • Dirty readP1 · ada dalam daftar ANSI SQL-92

    Membaca nilai yang sudah di-rollback

    dirty-read

    Sebuah pengembalian dana sedang diproses. Transaksi pengembalian itu sudah menurunkan saldo menjadi 0 ketika sebuah query pelaporan membacanya, lalu pengembalian itu gagal dan di-rollback.

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: Diizinkan padaREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: bersihREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    Pelajarannya

    ANSI mendefinisikan READ UNCOMMITTED sebagai level yang mengizinkan ini. PostgreSQL menerima namanya lalu memberi Anda READ COMMITTED, jadi query pelaporan melihat 100 di semua level — saldo yang benar-benar commit. MySQL InnoDB adalah kebalikannya: READ UNCOMMITTED-nya sungguhan, dan query itu membaca 0 di sana — saldo yang tidak pernah ada. SQL Server sama sungguhannya. Oracle menolak nama levelnya sama sekali: ia tidak punya READ UNCOMMITTED dan tidak pernah mengizinkan dirty read.

    Buka di lab
  • Dirty writeP0 · tidak ada dalam daftar ANSI SQL-92

    Dua transaksi menulis baris yang sama sebelum salah satunya berakhir

    dirty-write

    Dua administrator menyunting pengaturan yang sama pada waktu yang sama. Suntingan yang pertama masih belum commit ketika yang kedua menulis.

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: bersihREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    Pelajarannya

    Tulisan kedua menunggu transaksi pertama berakhir, bukan menimpa nilai yang belum commit — first updater wins. Karena transaksi pertama di-rollback, yang kedua melanjutkan dari baris aslinya.

    Buka di lab
  • Non-repeatable readP2 · ada dalam daftar ANSI SQL-92

    Baris yang sama, dibaca dua kali, dengan dua nilai berbeda

    non-repeatable-read

    Sebuah proses checkout membaca ulang harga barang untuk menghitung pajak setelah sebelumnya membacanya untuk menghitung subtotal. Di antara dua pembacaan itu, sebuah perubahan harga commit.

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: Diizinkan padaREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    Pelajarannya

    Pada READ COMMITTED setiap statement mengambil snapshot baru, jadi kedua pembacaan itu sah-sah saja berbeda. REPEATABLE READ mengambil satu snapshot untuk seluruh transaksi dan kedua pembacaan mengembalikan 100.

    Buka di lab
  • Read skewA5A · tidak ada dalam daftar ANSI SQL-92

    Sebuah transfer yang terlihat setengah jadi

    read-skew

    Rekening 1 dan 2 masing-masing berisi 100 dan invariannya adalah totalnya 200. Sebuah audit membaca rekening 1, lalu transfer 50 dari rekening 1 ke rekening 2 commit, lalu audit itu membaca rekening 2.

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: Diizinkan padaREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    Pelajarannya

    Audit itu membaca 100 lalu 150 dan melaporkan total 250. Tidak ada pembacaan yang melihat data belum commit dan kedua nilai itu sudah commit — hanya saja tidak pada waktu yang sama. Inilah sebabnya sebuah laporan butuh satu snapshot, bukan dua pembacaan yang masing-masing benar.

    Buka di lab
  • Lost updateP4 · tidak ada dalam daftar ANSI SQL-92

    Dua pengurangan stok menjadi satu

    lost-update

    Stok sepuluh unit. Dua pesanan masing-masing membaca stok, mengurangi satu, lalu menuliskannya kembali. Yang tercatat sembilan unit, dan satu unit terjual dua kali.

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: Diizinkan padaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: Diizinkan padaREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    Pelajarannya

    Tidak ada transaksi yang salah bila dilihat sendiri, tidak ada kesalahan yang muncul, dan hitungannya keliru. READ COMMITTED menerapkan ulang tulisan kedua ke versi yang baru commit, jadi ia menulis 9 di atas 9. REPEATABLE READ menolaknya dan membatalkan dengan 40001 — kesalahan yang bisa dicoba ulang oleh kode Anda. MySQL InnoDB mengizinkannya di REPEATABLE READ juga: DML-nya selalu bekerja pada baris terbaru yang commit dan tidak pernah menaikkan serialization error, jadi jadwal yang gagal berisik di PostgreSQL berhasil diam-diam di sana.

    Buka di lab
  • Lost updateP4 · tidak ada dalam daftar ANSI SQL-92

    Dua pengurangan yang sama, dengan barisnya di-lock

    lost-update-locked

    Dua pesanan yang sama, tetapi masing-masing membaca stok dengan SELECT ... FOR UPDATE sebelum menuliskannya kembali.

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: bersihREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    Pelajarannya

    Locking read pesanan kedua menunggu yang pertama commit. Pada READ COMMITTED ia lalu mengembalikan nilai baru, 9, jadi pengurangan kedua dihitung dari apa yang benar-benar ada. Pada REPEATABLE READ, locking read atas baris yang berubah di bawah snapshot justru dibatalkan dengan 40001 — perlindungan yang sama, disampaikan sebagai kesalahan.

    Buka di lab
  • Phantom readP3 · ada dalam daftar ANSI SQL-92

    Sebuah pemesanan yang muncul di tengah satu transaksi

    phantom-read

    Slot 1 dan 2 dari kalender lima slot sudah dipesan. Sebuah laporan menghitung jumlah pemesanan, lalu seseorang memesan slot 3 dan commit, lalu laporan itu menghitung lagi untuk menampilkan totalnya.

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: Diizinkan padaSNAPSHOT: bersihSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: Diizinkan padaSNAPSHOT: bersihSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: Diizinkan padaREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    Pelajarannya

    Pada READ COMMITTED hitungan kedua mengembalikan tiga baris padahal yang pertama dua. REPEATABLE READ pada PostgreSQL mencegah ini — yang tidak diwajibkan standar SQL untuk level itu, dan justru itulah sebabnya nama level tidak bisa dipercaya antar mesin. SQL Server menutup perdebatan itu: REPEATABLE READ-nya memegang shared lock atas baris yang dibaca tetapi tidak bisa me-lock baris yang belum ada, jadi phantom muncul di sana dan tidak di PostgreSQL.

    Buka di lab
  • Write skewA5B · tidak ada dalam daftar ANSI SQL-92

    Jadwal jaga yang sama, dibaca dengan FOR UPDATE

    write-skew-locked

    Dua dokter yang sama, tetapi masing-masing membaca kedua baris dengan SELECT ... FOR UPDATE sebelum menulis, yang biasanya disarankan ketika Anda tidak bisa memakai SERIALIZABLE.

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: bersihREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    Pelajarannya

    Lock itu men-serialkan kedua transaksi. Locking read dokter kedua menunggu, lalu mengembalikan jadwal yang di dalamnya dokter pertama sudah keluar, sehingga jadwal ini setara dengan menjalankan T1 lalu T2 — tidak ada lagi anomali yang bisa diizinkan basis data. Jadwal jaga tetap kosong, karena jadwal ini menulis tanpa memeriksa ulang apa yang dikembalikan locking read itu; bug yang tersisa itu milik aplikasi, dan sekarang terlihat pada nilai yang terbaca. Pada REPEATABLE READ, locking read itu dibatalkan dengan 40001.

    Buka di lab
  • Write skewA5B · tidak ada dalam daftar ANSI SQL-92

    Dua pemesanan untuk kalender yang kosong ketika keduanya melihat

    phantom-insert-race

    Belum ada yang memesan slot 1 sampai 5. Dua orang masing-masing memastikan rentang itu kosong, lalu masing-masing memesan slot yang berbeda di dalamnya.

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: Diizinkan padaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: Diizinkan padaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: Diizinkan padaSNAPSHOT: Diizinkan padaSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: Diizinkan padaREAD COMMITTED: Diizinkan padaREPEATABLE READ: Diizinkan padaSNAPSHOT: Diizinkan padaSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: Diizinkan padaREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: Diizinkan pada
    Pelajarannya

    Kedua range read tidak mengembalikan apa pun, dan kedua insert berhasil, jadi kalender berakhir dengan dua pemesanan padahal setiap pemesan yakin hanya akan ada satu. Tidak ada baris yang ditulis dua kali, jadi tidak ada yang berkonflik — ini write skew yang berpakaian phantom, dan hanya SERIALIZABLE yang menghentikannya.

    Buka di lab
  • Mendokumentasikan respons mesin, bukan sebuah anomali

    Dua transfer yang me-lock rekening yang sama dengan urutan berlawanan

    deadlock

    Dua transfer berjalan bersamaan. Yang satu memindahkan uang dari rekening 1 ke rekening 2 dan me-lock keduanya dengan urutan itu; yang lain memindahkan dari 2 ke 1 dan me-lock dengan urutan sebaliknya. Masing-masing memegang apa yang berikutnya dibutuhkan yang lain.

    Diizinkan pada

    RURCRRSISER
    PostgreSQL 16READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    MySQL InnoDB 8.4READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    SQL Server 2022READ UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    SQL Server 2022 · RCSIREAD UNCOMMITTED: bersihREAD COMMITTED: bersihREPEATABLE READ: bersihSNAPSHOT: bersihSERIALIZABLE: bersih
    Oracle 23aiREAD UNCOMMITTED: level tidak tersediaREAD COMMITTED: bersihREPEATABLE READ: level tidak tersediaSNAPSHOT: level tidak tersediaSERIALIZABLE: bersih
    Pelajarannya

    Tidak ada transaksi yang melakukan hal aneh, dan tidak ada isolation level yang mencegah ini — me-lock dengan urutan yang konsisten adalah tugas aplikasi. Yang berbeda adalah jawaban mesinnya. PostgreSQL dan MySQL InnoDB me-rollback satu transaksi dan menyebutkannya, sehingga yang lain bisa lanjut. SQL Server memilih korbannya berdasarkan perkiraan biaya internal, dan Oracle menyatakan terang-terangan bahwa session mana pun bisa mendapat error itu — jadi untuk ketiganya model ini menolak menyebut siapa yang kalah daripada mengarangnya, dan Oracle pun hanya me-rollback statement-nya, membiarkan transaksinya tetap terbuka.

    Buka di lab

Apa ini, tepatnya

Ini memodelkan perilaku yang terdokumentasi untuk sekumpulan operasi tetap pada versi mesin tertentu. Ini bukan basis data. Apa pun di luar himpunan yang dimodelkan ditolak, bukan didekati, dan setiap klaim tentang mesin tertaut ke dokumentasi vendornya.

Designed & built by Andi Fathul Mukminin · © 2026