Kita sudah melihat transaksi menyelamatkan transfer dua langkah dari keadaan setengah jadi. Membuka transaksi di MySQL ternyata punya lebih dari satu pintu: START TRANSACTION, sinonim lamanya BEGIN, sampai mode SET autocommit = 0 yang mengubah cara kerja seluruh sesi. Setiap pintu punya konsekuensi berbeda — dan ada satu jebakan besar bernama implicit commit yang bisa membuat ROLLBACK kehilangan dayanya tanpa peringatan. Pada chapter ini kita uji ketiga cara membuka transaksi secara nyata, mengamati apa yang terjadi pada transaksi yang belum ditutup saat koneksi terputus, lalu membuktikan bahwa statement DDL diam-diam meng-commit transaksi yang sedang berjalan.
Menyiapkan Tabel Latihan #
Database latihan belajar_trx kembali menemani kita dengan tabel rekening berisi saldo dua nasabah:
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
);
INSERT INTO rekening (pemilik, saldo) VALUES ('Budi', 500000.00), ('Siti', 300000.00);
SELECT id, pemilik, saldo FROM rekening ORDER BY id;Output yang Dihasilkan:
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, 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)START TRANSACTION: Membuka Transaksi Eksplisit #
START TRANSACTION adalah cara baku membuka transaksi eksplisit: autocommit ditangguhkan, seluruh statement DML setelahnya terkumpul dalam satu unit kerja, dan unit kerja tersebut menunggu COMMIT atau ROLLBACK. Mari kita amati siklusnya dalam satu rangkaian:
START TRANSACTION;
UPDATE rekening SET saldo = saldo - 100000 WHERE id = 1;
SELECT id, pemilik, saldo FROM rekening ORDER BY id;
ROLLBACK;
SELECT id, pemilik, saldo FROM rekening ORDER BY id;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
+----+---------+-----------+
| id | pemilik | saldo |
+----+---------+-----------+
| 1 | Budi | 400000.00 |
| 2 | Siti | 300000.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)SELECT pertama menunjukkan saldo Budi Rp400.000,00 — di dalam transaksi, perubahan kita sendiri langsung terlihat. ROLLBACK kemudian membatalkan perubahan itu, dan SELECT kedua menunjukkan saldo kembali Rp500.000,00. Perhatikan satu hal penting: transaksi berakhir otomatis begitu COMMIT atau ROLLBACK dijalankan. Sesi tidak “berdiri di dalam” transaksi secara permanen — setelah penutup, kita kembali ke mode autocommit sampai transaksi berikutnya dibuka.
BEGIN dan BEGIN WORK: Sinonim yang Sama Efeknya #
Sebelum START TRANSACTION distandarkan, MySQL sudah punya kata kunci BEGIN — dan BEGIN WORK sebagai variasinya. Keduanya masih sah di MySQL 8.4 dan efeknya identik dengan START TRANSACTION:
BEGIN;
UPDATE rekening SET saldo = saldo - 50000 WHERE id = 1;
ROLLBACK;
SELECT saldo FROM rekening WHERE id = 1;
BEGIN WORK;
UPDATE rekening SET saldo = saldo + 25000 WHERE id = 1;
ROLLBACK;
SELECT saldo FROM rekening WHERE id = 1;Output yang Dihasilkan:
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, 0 rows affected (0.00 sec)
+-----------+
| saldo |
+-----------+
| 500000.00 |
+-----------+
1 row in set (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
Query OK, 0 rows affected (0.00 sec)
+-----------+
| saldo |
+-----------+
| 500000.00 |
+-----------+
1 row in set (0.00 sec)Kedua ROLLBACK berhasil membatalkan UPDATE masing-masing, dan saldo Budi tetap Rp500.000,00 di kedua SELECT penutup. Kode warisan yang memakai BEGIN karenanya tetap bekerja; untuk kode baru, START TRANSACTION lebih disukai karena namanya eksplisit dan tidak bentrok dengan penggunaan kata BEGIN di stored program.
Satu akibat dari aturan “transaksi berakhir saat ditutup” perlu kita buktikan: membuka transaksi baru saat masih ada transaksi terbuka akan meng-commit transaksi lama secara diam-diam:
START TRANSACTION;
UPDATE rekening SET saldo = saldo + 80000 WHERE id = 1;
START TRANSACTION;
UPDATE rekening SET saldo = saldo + 20000 WHERE id = 1;
ROLLBACK;
SELECT saldo FROM rekening WHERE id = 1;Output yang Dihasilkan:
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, 0 rows affected (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
Query OK, 0 rows affected (0.00 sec)
+-----------+
| saldo |
+-----------+
| 580000.00 |
+-----------+
1 row in set (0.00 sec)START TRANSACTION kedua menutup transaksi pertama dengan commit implisit, sehingga kenaikan Rp80.000,00 pada transaksi pertama menjadi permanen. ROLLBACK di akhir hanya membatalkan kenaikan Rp20.000,00 pada transaksi kedua — saldo akhir Rp580.000,00 adalah Rp500.000,00 ditambah Rp80.000,00. Setiap pemicu implicit commit seperti ini harus kita waspadai, dan pemicu terbesarnya dibedah di bagian DDL di bawah.
Mode SET autocommit = 0: Transaksi Implisit Berkelanjutan #
Cara ketiga membuka transaksi mengubah perilaku sesi secara menyeluruh. Dengan SET autocommit = 0, MySQL berhenti meng-commit setiap statement: statement pertama langsung membuka transaksi implisit, dan transaksi tersebut terus hidup — melintasi statement demi statement — sampai kita menjalankan COMMIT atau ROLLBACK secara eksplisit:
SET autocommit = 0;
SELECT @@autocommit;
UPDATE rekening SET saldo = saldo + 111000 WHERE id = 2;
SELECT saldo FROM rekening WHERE id = 2;Output yang Dihasilkan:
Query OK, 0 rows affected (0.00 sec)
+--------------+
| @@autocommit |
+--------------+
| 0 |
+--------------+
1 row in set (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Rows matched: 1 Changed: 1 Warnings: 0
+-----------+
| saldo |
+-----------+
| 411000.00 |
+-----------+
1 row in set (0.00 sec)@@autocommit kini bernilai 0, dan SELECT di dalam blok menunjukkan saldo Siti Rp411.000,00 — perubahan kita sendiri tetap terlihat. Namun blok kode di atas sengaja tidak menutup transaksi, dan di sinilah perbedaannya muncul: transaksi yang masih terbuka saat koneksi terputus dibatalkan otomatis oleh server. Blok berikut membuka koneksi baru — situasi yang setara dengan aplikasi lain membuka koneksinya sendiri — dan membaca saldo Siti:
SELECT saldo FROM rekening WHERE id = 2;Output yang Dihasilkan:
+-----------+
| saldo |
+-----------+
| 300000.00 |
+-----------+
1 row in set (0.00 sec)Saldo Siti terbaca Rp300.000,00: kenaikan Rp111.000,00 lenyap karena sesi sebelumnya terputus tanpa COMMIT. Perilaku ini sebenarnya fitur keselamatan — transaksi setengah jadi akibat aplikasi crash tidak meninggalkan jejak — tetapi juga alasan mode ini berbahaya bila lupa menutup transaksi. Bagaimana bila kita mengembalikan autocommit ke 1 saat transaksi implisit masih berjalan? Perubahan yang tertunda ikut di-commit:
SET autocommit = 0;
UPDATE rekening SET saldo = saldo + 22000 WHERE id = 2;
SET autocommit = 1;
SELECT @@autocommit;Output yang Dihasilkan:
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, 0 rows affected (0.00 sec)
+--------------+
| @@autocommit |
+--------------+
| 1 |
+--------------+
1 row in set (0.00 sec)Mengaktifkan kembali autocommit berarti meng-commit transaksi terbuka, bukan membatalkannya. Koneksi baru berikutnya membuktikan hal itu:
SELECT saldo FROM rekening WHERE id = 2;Output yang Dihasilkan:
+-----------+
| saldo |
+-----------+
| 322000.00 |
+-----------+
1 row in set (0.00 sec)Saldo Siti kini Rp322.000,00 — kenaikan Rp22.000,00 dari blok sebelumnya tersimpan permanen. Kesimpulannya tegas: dalam mode autocommit = 0, penutupan transaksi selalu eksplisit — lewat COMMIT, ROLLBACK, atau pengalihan nilai autocommit — dan tidak pernah lewat sekadar berhenti menjalankan statement. Tabel berikut merangkum perbedaan dua pendekatan membuka transaksi:
| Aspek | START TRANSACTION / BEGIN | SET autocommit = 0 |
|---|---|---|
| Berlaku untuk | satu transaksi | seluruh sisa sesi |
| Statement pertama setelah penutup | kembali autocommit | transaksi implisit baru langsung terbuka |
| Titik berbahaya | lupa COMMIT/ROLLBACK | lupa menutup transaksi hingga koneksi terputus |
| Cocok untuk | operasi bisnis tunggal yang jelas batasnya | sesi skrip panjang yang memang selalu bertransaksi |
DDL Menyebabkan Implicit Commit #
Inilah jebakan yang paling sering melukai pemula: statement DDL seperti CREATE TABLE, ALTER TABLE, DROP TABLE, dan TRUNCATE TABLE menyebabkan implicit commit — MySQL meng-commit transaksi yang sedang terbuka sebelum menjalankan DDL, dan sesudahnya tidak membuka transaksi pengganti. Akibatnya, ROLLBACK yang kita rencanakan menjadi kosong. Kita buktikan dengan nyata:
START TRANSACTION;
UPDATE rekening SET saldo = saldo + 250000 WHERE id = 1;
CREATE TABLE log_sesi (id INT PRIMARY KEY, catatan VARCHAR(100));
ROLLBACK;
SELECT saldo FROM rekening WHERE id = 1;Output yang Dihasilkan:
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, 0 rows affected (0.00 sec)
Query OK, 0 rows affected (0.00 sec)
+-----------+
| saldo |
+-----------+
| 830000.00 |
+-----------+
1 row in set (0.00 sec)Output terakhir memperlihatkan saldo Budi Rp830.000,00 — padahal kita baru saja menjalankan ROLLBACK! Urutan peristiwanya: START TRANSACTION membuka transaksi, UPDATE menaikkan saldo Rp250.000,00 di dalam transaksi, lalu CREATE TABLE meng-commit transaksi tersebut sebelum membuat tabel log_sesi. Ketika ROLLBACK tiba, tidak ada lagi transaksi terbuka yang bisa dibatalkan, sehingga kenaikan saldo sudah permanen. Tabel yang dibuat pun tetap ada meskipun berada “setelah” pembiaran:
INSERT INTO log_sesi (id, catatan) VALUES (1, 'Migrasi saldo kuartal pertama');
SELECT id, catatan FROM log_sesi ORDER BY id;Output yang Dihasilkan:
Query OK, 1 row affected (0.00 sec)
+----+-------------------------------+
| id | catatan |
+----+-------------------------------+
| 1 | Migrasi saldo kuartal pertama |
+----+-------------------------------+
1 row in set (0.00 sec)Tabel log_sesi hidup dan menerima baris baru — DDL beserta seluruh transaksi yang mendahuluinya sudah ter-commit bersama-sama. Selain DDL, daftar pemicu implicit commit lain di MySQL mencakup sebagian besar statement administratif, termasuk LOCK TABLES dan UNLOCK TABLES yang kita bahas tuntas di chapter Locking — daftar lengkapnya tersedia di halaman referensi resmi.
Membersihkan Database Latihan #
Rangkaian latihan selesai dan database latihan kita jatuhkan kembali:
DROP DATABASE belajar_trx;Output yang Dihasilkan:
Query OK, 2 rows affected (0.00 sec)Referensi #
- START TRANSACTION, COMMIT, and ROLLBACK Statements — MySQL 8.4 Reference Manual
- The InnoDB Transaction Model — MySQL 8.4 Reference Manual
Tiga pintu membuka transaksi kini sudah kita lalui beserta jebakannya: START TRANSACTION untuk unit kerja tunggal, BEGIN sebagai sinonim lama, dan autocommit = 0 untuk sesi yang terus bertransaksi — plus bukti nyata bahwa DDL meng-commit transaksi secara diam-diam. Pintu hanya setengah dari cerita; dua penutup transaksi, COMMIT dan ROLLBACK, menyimpan kejutan lain berupa perilaku error di tengah transaksi dan visibilitas antar-koneksi. Chapter berikutnya mengukuhkan dan membatalkan transaksi secara penuh.