Selama ini kita menulis INSERT dan UPDATE tanpa BEGIN, dan semuanya langsung tersimpan begitu statement selesai. Perilaku ini bernama autocommit: mode bawaan SQLite di mana setiap statement adalah transaksinya sendiri. Chapter ini membedah cara kerja mode tersebut, cara mengambil alih kendali secara manual, dan mengapa membiarkan ribuan komit kecil berjalan bisa sangat lambat.
Setiap Statement Adalah Transaksi Sendiri #
Tanpa BEGIN yang terbuka, engine SQLite membungkus setiap statement dengan pasangan BEGIN dan COMMIT implisit. Tabel contoh kita untuk chapter ini adalah produk:
CREATE TABLE produk (
id INTEGER PRIMARY KEY,
nama TEXT NOT NULL,
harga INTEGER NOT NULL
);Dua perintah berikut dijalankan dari terminal sebagai koneksi terpisah — dan setiap koneksi menyelesaikan satu transaksi penuh:
sqlite3 toko.db "INSERT INTO produk (nama, harga) VALUES ('Kopi Gayo 200g', 45000);"
sqlite3 toko.db "INSERT INTO produk (nama, harga) VALUES ('Gula Aren 500g', 30000);"
sqlite3 toko.db "SELECT id, nama, harga FROM produk ORDER BY id;"Output yang Dihasilkan:
1|Kopi Gayo 200g|45000
2|Gula Aren 500g|30000Setelah masing-masing INSERT selesai, tidak ada jendela waktu untuk membatalkan — perubahan sudah permanen. Bahkan SELECT pun berjalan sebagai transaksi baca singkat: kunci dibuka, data dibaca, kunci dilepas.
Beralih ke Kontrol Manual #
BEGIN memindahkan koneksi ke mode manual: autocommit mati hingga COMMIT atau ROLLBACK datang. Mari kita buka transaksi dan sisipkan satu produk lagi:
BEGIN;
INSERT INTO produk (nama, harga) VALUES ('Teh Melati 25 sachet', 15000);Dari terminal kedua, baris ketiga ini belum terlihat:
sqlite3 toko.db "SELECT COUNT(*) FROM produk;"Output yang Dihasilkan:
2Kembali ke koneksi pertama dan akhiri transaksinya:
COMMIT;sqlite3 toko.db "SELECT COUNT(*) FROM produk;"Output yang Dihasilkan:
3Sebagai catatan sintaksis, END adalah sinonim resmi COMMIT — keduanya identik secara perilaku:
BEGIN;
INSERT INTO produk (nama, harga) VALUES ('Keripik Singkong 100g', 12000);
END;
SELECT COUNT(*) FROM produk;Output yang Dihasilkan:
4Mendeteksi Status Transaksi #
sqlite3 CLI tidak menyediakan PRAGMA untuk memeriksa apakah sebuah koneksi sedang berada di dalam transaksi — kita cukup melacak BEGIN yang pernah diketik. Di sisi aplikasi, modul sqlite3 milik Python menyediakan atribut in_transaction:
import sqlite3
con = sqlite3.connect(":memory:", isolation_level=None)
con.execute("CREATE TABLE t(x)")
print(con.in_transaction)
con.execute("BEGIN")
con.execute("INSERT INTO t VALUES (1)")
print(con.in_transaction)
con.execute("COMMIT")
print(con.in_transaction)Output yang Dihasilkan:
False
True
FalseAtribut in_transaction berubah menjadi True sejak BEGIN hingga COMMIT atau ROLLBACK. Parameter isolation_level=None membuat Python berperilaku persis seperti CLI — tanpa parameter ini, modul sqlite3 diam-diam membungkus setiap INSERT, UPDATE, dan DELETE dengan transaksinya sendiri.
Harga Sebuah Komit #
Setiap komit di mode rollback journal melakukan pekerjaan fisik yang nyata: berkas journal dibuat, isi lama disalin, keduanya di-fsync ke media penyimpanan, lalu journal dihapus. Mengulang siklus ini 1.000 kali berarti 1.000 kali kerja tersebut. Perbandingannya mudah diukur:
SQL=$(for i in $(seq 1 1000); do echo "INSERT INTO stok (nama, harga) VALUES ('Produk $i', $((1000+i)));"; done)
rm -f waktu.db
time sqlite3 waktu.db "CREATE TABLE stok (id INTEGER PRIMARY KEY, nama TEXT NOT NULL, harga INTEGER NOT NULL); $SQL"
rm -f waktu.db
time sqlite3 waktu.db "CREATE TABLE stok (id INTEGER PRIMARY KEY, nama TEXT NOT NULL, harga INTEGER NOT NULL); BEGIN; $SQL COMMIT;"
rm -f waktu.dbKedua perintah menyisipkan 1.000 baris yang sama persis; bedanya hanya bungkus BEGIN ... COMMIT. Di mesin penulis, versi autocommit memakan waktu sekitar 33 milidetik, sedangkan versi satu transaksi selesai dalam 3 milidetik — sekitar sepuluh kali lebih cepat. Pada harddisk yang lebih lambat dalam menangani fsync, rasionya kerap mencapai puluhan hingga ratusan kali.
Satu konsekuensi lain dari autocommit: dua proses yang sama-sama menulis akan saling berhadapan dengan galat database is locked. Bagaimana SQLite mengatur antrean penulis dan pembaca ini dibahas pada Chapter Isolation dan Locking.