Apa itu transaksi database?
(planetscale.com)- Transaksi adalah struktur untuk menjalankan beberapa operasi di database sebagai satu unit atomik, mencakup baca, tulis, pembaruan, dan penghapusan
- MySQL dan Postgres mengontrol transaksi dengan
begin;dancommit;, lalu membatalkan perubahan denganrollback;saat terjadi kegagalan atau error - Kedua database sama-sama menjamin consistent read, tetapi Postgres menggunakan MVCC (Multi-Version Concurrency Control), sedangkan MySQL menggunakan undo log
- Isolation level mengontrol interferensi data antartransaksi, dan dibagi menjadi empat tingkat dari Serializable hingga Read Uncommitted
- Postgres dan MySQL menangani konflik penulisan bersamaan dengan cara berbeda; Postgres memakai validasi optimistis, sementara MySQL menggunakan row-level locking
Konsep dasar transaksi
- Transaksi adalah struktur yang mengelompokkan beberapa operasi SQL di database menjadi satu unit eksekusi atomik
- Dimulai dengan
begin;dan diakhiri dengancommit;, dengan beberapa kueri dapat dijalankan di antaranya - Pada saat
commit;, semua perubahan diterapkan sekaligus
- Dimulai dengan
- Saat terjadi gangguan tak terduga (mati listrik, error disk, dan sebagainya) atau pembatalan yang disengaja, perubahan dapat dikembalikan dengan
rollback;- Postgres mendukung pemulihan dengan WAL (Write-Ahead Log)
- Data yang diubah selama transaksi akan terisolasi dan tidak terlihat dari sesi lain
- Saat
rollback;, semua perubahan dibatalkan sehingga database kembali ke keadaan semula
- Saat
Consistent read
- Transaksi harus mempertahankan tampilan data yang konsisten yang tidak terpengaruh oleh perubahan eksternal selama eksekusi
- MySQL dan Postgres mendukung hal ini pada mode
REPEATABLE READke atas, tetapi implementasinya berbeda- Postgres: mengelola versi setiap baris dengan MVCC (Multi-Version Concurrency Control)
- MySQL: merekonstruksi versi lama dengan undo log
Penyimpanan multi-versi baris di Postgres
- Setiap kali baris diperbarui, versi baru dibuat; versi lama mencatat ID transaksi di
xmax, dan versi baru dixmin - Sebelum transaksi di-commit, sesi lain tidak dapat melihat perubahan tersebut
- Setelah commit, versi baru diterapkan ke seluruh database
- Saat
rollback;, perubahan dibuang dan data asli tetap dipertahankan - Versi baris lama dibersihkan dengan perintah
VACUUM FULLuntuk merebut kembali ruang penyimpanan
Undo log di MySQL
- MySQL menimpa baris secara langsung, tetapi mencatat nilai sebelumnya di undo log sehingga dapat dipulihkan bila diperlukan
- Setiap baris memiliki metadata
xid(ID transaksi modifikasi terbaru) danptr(pointer undo log) - Saat beberapa transaksi berjalan bersamaan, setiap transaksi secara selektif mengambil versi yang dibutuhkan melalui undo log
- Beberapa catatan undo log dapat ada untuk baris yang sama, dan versi yang sesuai dipilih berdasarkan ID transaksi
Isolation level
- Ini adalah pengaturan untuk mengontrol interferensi data antartransaksi, dengan tingkat pelonggaran berurutan Serializable → Repeatable Read → Read Committed → Read Uncommitted
- Serializable: semua transaksi berjalan seolah-olah dieksekusi secara berurutan
- Repeatable Read: hasil tetap sama saat kueri yang sama dijalankan ulang, tetapi phantom read masih mungkin terjadi
- Read Committed: dapat membaca perubahan dari transaksi lain yang sudah di-commit
- Read Uncommitted: mengizinkan dirty read, tingkat perlindungan paling rendah tetapi performanya tinggi
Penulisan bersamaan
- Cara menangani dua transaksi yang memodifikasi baris yang sama secara bersamaan berbeda-beda tergantung database
MySQL: Row-level locking
- Shared lock (S lock) memungkinkan beberapa transaksi membaca secara bersamaan
- Exclusive lock (X lock) hanya memungkinkan satu transaksi memodifikasi baris
- Dalam mode
SERIALIZABLE, setiap pembaruan harus memperoleh X lock, dan deadlock dapat terjadi saat ada konflik - MySQL mendeteksi deadlock dan menghentikan salah satu transaksi
Postgres: Serializable Snapshot Isolation
- Postgres menggunakan predicate lock untuk melacak akses pada tingkat himpunan baris
- Contoh: lock untuk kondisi
WHERE id BETWEEN 10 AND 20
- Contoh: lock untuk kondisi
- Akses nyata tidak langsung diblokir; sistem mendeteksi konflik dan menghentikan transaksi bila terjadi pelanggaran
- Dengan optimistic conflict resolution, deadlock dapat dihindari
- Sama seperti MySQL, saat konflik terjadi satu transaksi akan dihentikan dan aplikasi perlu menerapkan logika retry
Kesimpulan
- Transaksi adalah komponen inti database yang menjamin ACID (Atomicity, Consistency, Isolation, Durability)
- Postgres dan MySQL mencapai tujuan yang sama dengan struktur internal yang berbeda
- Memahami empat isolation level dan cara kerja transaksi membantu pengoperasian database yang lebih stabil
1 komentar
Komentar Hacker News
Tulisan ini terasa agak kurang memadai.
Menurut saya, akan lebih intuitif bila penjelasan level isolasi dimulai dari konsep serializability, alih-alih berpusat pada phenomena yang didefinisikan dalam standar SQL.
Serializability bisa dilihat sebagai generalisasi dari thread safety, dan jika ini hilang, akan muncul bug yang hasilnya berubah tergantung urutan eksekusi.
Berbagai level isolasi di database pada dasarnya hanyalah bentuk pelonggaran dari jaminan ini, sehingga pengguna harus memastikan jaminan tersebut dengan cara lain.
Phenomena hanyalah alat untuk memvisualisasikan situasi non-serial, bukan sesuatu yang terhubung langsung dengan serializability.
Misalnya, cluster Kubernetes pun bisa beroperasi secara serializable jika menggunakan controller yang dirancang dengan baik.
Membahas transaksi, level isolasi, dan MVCC sekaligus, bahkan sampai membandingkan beberapa DB, adalah pekerjaan yang sangat besar.
Saya berusaha menyeimbangkan kedalaman teknis, aksesibilitas, dan panjang tulisan.
Menurutnya akan lebih baik jika ada lebih banyak notasi dan kutipan.
Namun jika dipakai tanpa perlu, biaya koordinasi antar transaksi meningkat sehingga konkurensi dan throughput menurun.
Transaksi juga bisa dibayangkan seperti snapshot pada filesystem copy-on-write (btrfs, zfs), tetapi menurut saya analogi branch Git lebih intuitif.
BEGIN adalah membuat branch, UPDATE adalah commit, ROLLBACK adalah menghapus branch, dan COMMIT sama seperti
git merge.Jika terjadi konflik, DB akan mencoba melakukan merge per baris, dan jika gagal, akan rollback atau memaksa merge tergantung konfigurasi.
READ UNCOMMITTED mengutamakan merge cepat, sedangkan SERIALIZABLE mengutamakan akurasi.
Analogi seperti ini bisa membantu seseorang mendapat momen “aha!” dalam memahami konsep transaksi.
Hal yang membuat banyak orang terkejut adalah Postgres dan MySQL secara default bukan mode serializable, melainkan read committed.
Perbedaan performanya bukan sekadar “sedikit”, tetapi jauh lebih besar dalam praktiknya.
Jika memakai read committed, kita perlu memperhatikan pengelolaan lock, dan constraint UNIQUE juga diperlukan untuk mencegah race condition.
Meski begitu, cara ini tetap lebih disukai daripada harus menanggung penurunan performa dan masalah retry pada mode serializable.
Referensi: dokumentasi resmi PostgreSQL
Lihat dokumentasi MySQL dan dokumentasi MariaDB.
MyISAM sekarang hampir tidak dipakai.
Aplikasi harus bisa mendeteksinya dan memiliki strategi retry.
Mode serializable terlihat keren di buku teks, tetapi dalam praktik hampir tidak dipakai.
Banyak tool database belakangan ini lebih memprioritaskan berbagi pembaruan real-time daripada ACID.
Misalnya, di Airtable perubahan field langsung muncul di layar rekan kerja, tetapi karena tidak ada transaksi, ada risiko inkonsistensi data.
Terkait hal ini, lihat tulisan blog VisualDB.
Membaca blog PlanetScale benar-benar menyenangkan.
Penasaran tool apa yang dipakai untuk visualisasinya.
Visualisasinya dibuat dengan js + gsap(https://gsap.com).
Jika tertarik pada topik ini, saya sangat merekomendasikan 『Designing Data-Intensive Applications』.
Buku itu membahas bukan hanya berbagai level isolasi, tetapi juga ambigu definisi ACID.
Katanya edisi kedua akan segera terbit.
Transaksi pada sistem MVCC seperti Postgres mirip dengan snapshot pada filesystem copy-on-write.
Saat BEGIN, snapshot data dibuat, dan UPDATE hanya diterapkan pada salinan privat.
Saat ROLLBACK, salinan itu dibuang, dan saat COMMIT, snapshot baru menjadi versi resmi.
Analogi ini bisa menjadi titik yang membuat seseorang benar-benar memahami konsep transaksi dengan jelas.
P.S. Analogi branch Git juga memungkinkan.
Dalam kasus seperti SELECT lalu UPDATE, satu thread bisa saja terblokir.
Hari ini saya berencana bereksperimen di MySQL untuk melihat apakah ini bisa diubah menjadi satu query saja.
Dulu transaksi sering ditanyakan saat wawancara backend.
Semua orang pernah memakainya, tetapi tingkat pemahamannya berbeda-beda tergantung pengalaman.
Walau tidak hafal semua level isolasi, hanya dengan tahu bahwa masing-masing berperilaku berbeda saja sudah bisa menunjukkan rasa ingin tahu dan pemahaman sistem.
Penjelasan tentang “phantom read” bisa menimbulkan salah paham.
Pada repeatable read, nilai baris yang sudah ada tidak berubah, tetapi baris baru bisa ditambahkan.
Karena baris yang ada tidak berubah atau dihapus, poin itu perlu dijelaskan dengan tegas.
Kalimat “tidak ada hubungannya dengan xmin/xmax” terasa tidak lengkap.
Visualisasi yang menunjuk ke header tabel saat commit juga terasa aneh.
Bukankah sebenarnya xmax/xmin adalah mekanisme inti untuk menentukan apakah suatu commit sudah berlaku?
Jika subtransaksi juga dipertimbangkan, jadinya lebih rumit.
Meski begitu, visualisasi dan penjelasannya secara keseluruhan tetap menyenangkan untuk dibaca.
Itu penting untuk memahami level isolasi, sampai terasa seperti ada satu bagian yang hilang.