Empat perintah inti transaksi — START TRANSACTION, COMMIT, ROLLBACK, dan SAVEPOINT — kini sudah kita kuasai. Pertanyaan tingkat berikutnya: jaminan apa sesungguhnya yang kita beli dengan perintah-perintah itu? Industri database merangkumnya dalam empat huruf: Atomicity, Consistency, Isolation, dan Durability — Akronim ACID. Keempatnya bukan slogan pemasaran; tiap huruf punya mekanisme nyata di dalam InnoDB, mulai dari undo log sampai redo log. Pada chapter ini kita buktikan dua huruf pertama secara langsung di terminal, jelaskan dua sisanya lewat skenario antar-koneksi, lalu rangkum semuanya dalam satu tabel rujukan.

Menyiapkan Tabel Latihan #

Skema rekening dan mutasi kembali dipakai — foreign key di antara keduanya akan menjadi bahan uji huruf C:

sql
DROP DATABASE IF EXISTS belajar_trx;
CREATE DATABASE belajar_trx;
USE belajar_trx;
CREATE TABLE rekening (
  id INT AUTO_INCREMENT PRIMARY KEY,
  pemilik VARCHAR(50) NOT NULL,
  saldo DECIMAL(12,2) NOT NULL
);
CREATE TABLE mutasi (
  id INT AUTO_INCREMENT PRIMARY KEY,
  rekening_id INT NOT NULL,
  jumlah DECIMAL(12,2) NOT NULL,
  keterangan VARCHAR(100) NOT NULL,
  tanggal DATE NOT NULL,
  CONSTRAINT fk_mutasi_rekening FOREIGN KEY (rekening_id) REFERENCES rekening(id)
);
INSERT INTO rekening (pemilik, saldo) VALUES ('Budi', 500000.00), ('Siti', 300000.00);
SELECT id, pemilik, saldo FROM rekening ORDER BY id;

Output yang Dihasilkan:

text
Query OK, 0 rows affected, 1 warning (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Database changed
Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

+----+---------+-----------+
| id | pemilik | saldo     |
+----+---------+-----------+
|  1 | Budi    | 500000.00 |
|  2 | Siti    | 300000.00 |
+----+---------+-----------+
2 rows in set (0.00 sec)

Atomicity: Semua atau Tidak Sama Sekali #

Atomicity menjanjikan transaksi tak terbelah: seluruh statement di dalamnya berlaku bersama, atau tidak satu pun berlaku. Janji ini terdengar abstrak sampai kita ingat skenario crash di tengah transfer — dan buktinya bisa kita lihat langsung. Empat statement berikut menjalankan transfer Rp100.000,00 lengkap dengan mutasinya, lalu seluruhnya kita batalkan:

sql
START TRANSACTION;
UPDATE rekening SET saldo = saldo - 100000 WHERE id = 1;
UPDATE rekening SET saldo = saldo + 100000 WHERE id = 2;
INSERT INTO mutasi (rekening_id, jumlah, keterangan, tanggal) VALUES
  (1, -100000.00, 'Transfer ke Siti', '2026-01-15'),
  (2, 100000.00, 'Transfer dari Budi', '2026-01-15');
SELECT id, pemilik, saldo FROM rekening ORDER BY id;
ROLLBACK;
SELECT id, pemilik, saldo FROM rekening ORDER BY id;
SELECT COUNT(*) AS jumlah_mutasi FROM mutasi;

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

+----+---------+-----------+
| id | pemilik | saldo     |
+----+---------+-----------+
|  1 | Budi    | 400000.00 |
|  2 | Siti    | 400000.00 |
+----+---------+-----------+
2 rows in set (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

+----+---------+-----------+
| id | pemilik | saldo     |
+----+---------+-----------+
|  1 | Budi    | 500000.00 |
|  2 | Siti    | 300000.00 |
+----+---------+-----------+
2 rows in set (0.00 sec)

+---------------+
| jumlah_mutasi |
+---------------+
|             0 |
+---------------+
1 row in set (0.00 sec)

SELECT pertama menampilkan keadaan puncak transaksi — saldo Rp400.000,00 dan Rp400.000,00 — namun ROLLBACK menghapus keempat jejaknya sekaligus: saldo kembali Rp500.000,00 dan Rp300.000,00, tabel mutasi kembali kosong. Tidak ada statement yang “setengah batal”: pemotongan Budi dan penambahan Siti menghilang bersama-sama dalam satu tarikan. Mekanismenya ada di undo log InnoDB — setiap perubahan membekalkan catatan cara memulihkannya, sehingga pembatalan transaksi berapapun panjangnya tinggal memutar balik catatan demi catatan. Bayangkan pelayan restoran yang mencatat setiap pesanan di kertas: kertas itulah yang membuat seluruh meja bisa “dibatalkan” serentak.

Consistency: Aturan Tidak Bisa Ditembus dari Dalam Transaksi #

Consistency menjamin transaksi membawa database dari satu keadaan sah ke keadaan sah berikutnya: constraint, tipe data, dan aturan bisnis tetap dihormati di setiap titik. Bukan hanya setelah COMMIT — aturan juga diperiksa saat statement di dalam transaksi berjalan. Kita buktikan dengan mencatat mutasi untuk rekening yang tidak ada:

sql
-- @error
START TRANSACTION;
UPDATE rekening SET saldo = saldo + 50000 WHERE id = 1;
INSERT INTO mutasi (rekening_id, jumlah, keterangan, tanggal) VALUES (99, 50000.00, 'Bonus', '2026-01-17');
ROLLBACK;
SELECT saldo FROM rekening WHERE id = 1;

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`belajar_trx`.`mutasi`, CONSTRAINT `fk_mutasi_rekening` FOREIGN KEY (`rekening_id`) REFERENCES `rekening` (`id`))
Query OK, 0 rows affected (0.00 sec)

+-----------+
| saldo     |
+-----------+
| 500000.00 |
+-----------+
1 row in set (0.00 sec)

ERROR 1452 menolak INSERT seketika: foreign key fk_mutasi_rekening menyatakan rekening 99 tidak ada, dan pemeriksaan ini tidak mau menunggu COMMIT. Transaksi tetap terbuka setelah error — kita sudah membuktikan aturan ini di chapter sebelumnya — sehingga kenaikan saldo Rp50.000,00 masih menunggu keputusan; ROLLBACK lalu memulihkan saldo ke Rp500.000,00. Pelajaran pentingnya: berada di dalam transaksi bukan tiket menembus aturan. Sebaliknya, transaksi menolak keadaan tidak sah agar tidak pernah ter-commit — inilah yang membuat “saldo total selalu seimbang” pada skenario transfer.

Isolation: Pembaca Tidak Melihat Kerja yang Belum Selesai #

Isolation mengatur seberapa banyak pekerjaan satu transaksi terlihat oleh transaksi lain yang berjalan bersamaan. Standar resmi mengenal tiga anomali yang bisa muncul bila isolation terlalu longgar. Dirty read — pembacaan data yang belum di-commit: koneksi A menaikkan saldo menjadi Rp600.000,00 di dalam transaksi, koneksi B di level READ UNCOMMITTED membaca angka itu, lalu koneksi A membatalkan transaksinya — laporan koneksi B ternyata mengutip angka yang tidak pernah ada. Nonrepeatable read — satu baris terbaca berbeda antar-dua kali baca dalam transaksi yang sama. Phantom read — kumpulan baris hasil query berubah karena transaksi lain menyisipkan baris baru.

Anomali mana yang mungkin terjadi bergantung pada isolation level — pengaturan per-sesi yang kita bedah tuntas di chapter berikutnya. Untuk sekarang, kita cukup mencatat level default yang dipakai server kita:

sql
SELECT @@transaction_isolation;

Output yang Dihasilkan:

text
+-------------------------+
| @@transaction_isolation |
+-------------------------+
| REPEATABLE-READ         |
+-------------------------+
1 row in set (0.00 sec)

Nilai REPEATABLE-READ adalah level bawaan InnoDB — level yang menolak dirty read sejak awal dan menjaga hasil bacaan tetap konsisten sepanjang transaksi berkat mekanisme multiversion concurrency control (MVCC): pembacaan biasa tidak menunggu kunci penulis, karena tiap pembaca bekerja pada snapshot data yang dibekukan saat transaksinya dimulai.

Durability: Hasil COMMIT Bertahan Kehancuran #

Durability menjanjikan sesuatu yang berani: begitu COMMIT berhasil, hasilnya bertahan meski server mati listrik sedetik kemudian. InnoDB menepati janji ini lewat redo log — catatan perubahan berbentuk urutan tulis-append yang murah ditulis. Saat COMMIT berjalan, InnoDB menulis catatan perubahan transaksi ke redo log dan memastikan catatan itu sampai ke penyimpanan permanen (perilaku yang dikendalikan variabel innodb_flush_log_at_trx_commit, bawaannya mem-flush di setiap commit) — langkah ini yang membuat COMMIT lebih mahal daripada statement biasa, sekaligus yang membuat hasilnya tak tergoyahkan. Bila server crash, proses crash recovery saat menyala kembali memutar ulang redo log dan menyelesaikan tulisan yang belum sempat sampai ke tabel. Perlindungan lapis kedua bernama doublewrite buffer menjaga dari tulisan setengah jadi pada level blok disk.

Empat huruf itu kini punya wajah. Tabel berikut merangkum jaminan tiap huruf beserta mekanisme MySQL yang mengawaki:

HurufJaminanMekanisme MySQL/InnoDB
AtomicitySemua statement berlaku atau tidak sama sekaliTransaksi + undo log untuk pembatalan
ConsistencyAturan dan constraint tetap dihormati di setiap titikPemeriksaan constraint langsung + penolakan transaksi tidak sah
IsolationTransaksi bersamaan tidak saling menggangguMVCC + kunci baris + isolation level per-sesi
DurabilityHasil COMMIT selamat dari crashRedo log dengan flush per-commit + doublewrite buffer

Membersihkan Database Latihan #

Rangkaian latihan selesai dan database latihan kita jatuhkan kembali:

sql
DROP DATABASE belajar_trx;

Output yang Dihasilkan:

text
Query OK, 2 rows affected (0.00 sec)

Referensi #

ACID kini bukan sekadar akronim: atomicity kita lihat membatalkan empat statement sekaligus, consistency menahan pelanggaran foreign key di tengah transaksi, isolation bekerja lewat MVCC dan snapshot, dan durability ditopang redo log yang di-flush pada tiap commit. Dari empat huruf tersebut, isolation adalah yang paling kaya variasi — empat level dengan kombinasi anomali berbeda siap diuji. Chapter berikutnya membedah keempat isolation level MySQL satu per satu, lengkap dengan tabel anomali dan skenario dua koneksi.