Menu

Mode WAL dan Konkurensi SQLite: Pembaca, Penulis, Checkpoint

Cara write-ahead logging di SQLite mengubah urusan konkurensi: pembaca dan penulis tidak lagi saling menghalangi, dan apa sebenarnya fungsi file -wal dan -shm.

Halaman ini dilengkapi editor yang bisa dijalankan. Edit, jalankan, dan lihat output-nya seketika.

Mode Default dan Batasannya

Secara default, SQLite memakai rollback journal. Saat kamu menulis, SQLite menyalin halaman aslinya ke file -journal, mengubah database utama, lalu menghapus journal saat commit. Jika prosesnya crash di tengah penulisan, journal diputar ulang secara terbalik untuk membatalkan perubahan yang setengah jadi.

Cara ini sederhana dan aman, tapi punya satu sifat yang menyakitkan: penulis dan pembaca berebut file yang sama. Selama penulis memegang kunci database, tidak ada pembaca yang bisa memulai transaksi baru. Selama ada pembaca yang aktif, penulis harus menunggu. Di aplikasi yang sibuk, misalnya web server dengan beberapa request serentak, kamu akan melihat error SQLITE_BUSY lebih cepat dari yang kamu harapkan.

Mode WAL mengubah hal itu.

Apa yang Sebenarnya Dilakukan WAL

Write-ahead logging membalik modelnya. Alih-alih mengubah file database utama di tempat, penulis menambahkan halaman yang sudah di-commit ke file terpisah berakhiran -wal. Pembaca tetap membaca file utama, tapi juga mengintip WAL untuk melihat versi terbaru dari halaman yang mereka butuhkan.

Hasilnya: satu penulis dan berapa pun pembaca bisa aktif pada saat yang sama. Setiap pembaca melihat snapshot yang konsisten dari saat transaksinya dimulai, dan penulis sibuk menambah ke WAL tanpa menyentuh apa yang sedang dilihat pembaca.

Satu pragma itu sudah mengubah database-nya. Mode ini permanen: disimpan di header file, jadi setiap koneksi berikutnya otomatis memakai WAL. Kamu tidak perlu menjalankannya di setiap koneksi, cukup sekali saat menyiapkan database (atau di runner migrasimu).

Pragma ini mengembalikan mode baru. Jika hasilnya wal, semuanya beres. Jika hasilnya lain, kemungkinan sistem file-nya tidak mendukung shared memory (dibahas lebih lanjut di bawah).

Mengaktifkan dan Memastikan

Kamu bisa mengecek mode saat ini kapan saja:

Pemanggilan pertama mengaktifkan WAL dan mengembalikan mode baru. Pemanggilan kedua (tanpa =) hanya menanyakannya. Setelah ini, direktori messages.db-mu akan berisi tiga file saat ada aktivitas: messages.db, messages.db-wal, dan messages.db-shm. Dua yang terakhir muncul dan hilang tergantung apakah ada koneksi yang terbuka.

File -wal dan -shm

Ada dua file tambahan yang datang bersama WAL, dan kamu perlu tahu fungsinya:

  • -wal berisi transaksi yang sudah di-commit tapi belum digabungkan kembali ke database utama. File ini membesar seiring penulisan dan mengecil (atau direset) saat checkpoint.
  • -shm adalah file shared memory. Isinya index ke dalam WAL supaya semua koneksi sepakat halaman mana berada di mana tanpa memindai WAL pada setiap query.

Konsekuensi praktisnya: jangan pernah menyalin database bermode WAL hanya dengan menyalin file .db-nya. Data terbaru ada di -wal, dan tanpa file itu salinanmu usang atau rusak. Salin ketiga file saat tidak ada koneksi yang menulis, atau, jauh lebih baik, pakai backup API SQLite (dibahas di bab berikutnya).

Konkurensi: Satu Penulis, Banyak Pembaca

WAL tidak memberimu penulisan serentak. SQLite tetap menjalankannya satu per satu: pada setiap saat, tepat satu transaksi yang memegang kunci tulis. Yang berubah adalah penulisan tidak menghalangi pembacaan dan pembacaan tidak menghalangi penulisan.

Jadi aplikasi web biasa yang berjalan dengan WAL berperilaku seperti ini:

  • Endpoint yang banyak membaca berjalan paralel tanpa berebut.
  • Endpoint penulisan sempat mengantre sebentar di belakang satu sama lain, tapi tidak menghalangi pembacaan.
  • Pembaca yang berjalan lama (query analitik, ekspor) tidak membuat penulis menunggu.

Jika dua koneksi mencoba menulis pada saat yang sama, koneksi kedua mendapat SQLITE_BUSY. Solusinya biasanya busy timeout yang masuk akal: minta SQLite menunggu sebentar sebelum menyerah:

busy_timeout=5000 berarti "jika ada kunci yang sedang dipegang, tunggu sampai 5 detik sebelum memunculkan error." Dipadukan dengan WAL, ini menangani perebutan yang sebenarnya dihadapi sebagian besar aplikasi. Bentuk BEGIN IMMEDIATE mengambil kunci tulis di awal transaksi, bukan pada penulisan pertama, sehingga menghindari satu golongan deadlock saat kunci dinaikkan ketika beberapa koneksi sama-sama berniat menulis.

Checkpoint: Melipat WAL Kembali

File WAL tidak bisa membesar selamanya. Checkpoint adalah proses mengambil halaman yang sudah di-commit di WAL, menuliskannya ke database utama, lalu mereset WAL.

SQLite melakukan checkpoint otomatis saat WAL melewati sekitar 1000 halaman (nilai default wal_autocheckpoint). Untuk sebagian besar aplikasi kamu bisa membiarkannya. Jika ingin menyetelnya atau memicu checkpoint secara manual:

Pragma wal_checkpoint menerima sebuah mode:

  • PASSIVE: melakukan checkpoint sebanyak mungkin tanpa mengganggu pembaca/penulis. Ini default-nya.
  • FULL: menunggu penulis yang aktif selesai, lalu melakukan checkpoint untuk semua yang sudah di-commit.
  • RESTART: seperti FULL, ditambah menahan pembaca baru agar tidak memakai WAL lama.
  • TRUNCATE: seperti RESTART, ditambah mengecilkan file WAL kembali ke nol byte.

Sebagian besar server tidak pernah perlu memanggilnya secara manual. Jika kamu merilis aplikasi desktop yang ingin ukuran file-nya tetap rapi saat ditutup, checkpoint TRUNCATE sebelum menutup koneksi terakhir adalah kebiasaan yang masuk akal.

Beberapa Pragma yang Cocok Dipasangkan dengan WAL

WAL saja sudah bagus. WAL ditambah beberapa pengaturan lain biasanya itulah yang dipakai aplikasi produksi:

Penjelasan singkatnya:

  • synchronous=NORMAL adalah pasangan yang direkomendasikan untuk WAL. Pengaturan ini aman terhadap crash aplikasi dan crash OS; hanya listrik padam di saat yang tidak tepat yang bisa menghilangkan transaksi terbaru, dan bahkan saat itu database tetap konsisten. Default FULL lebih aman tapi terasa lebih lambat.
  • busy_timeout sudah kita bahas di atas.
  • foreign_keys=ON tidak berhubungan dengan WAL, tapi layak diatur di setiap koneksi: SQLite secara default mematikan penegakan foreign key demi kompatibilitas mundur.

Pengaturan ini berlaku per koneksi (kecuali journal_mode, yang menetap). Jalankan tepat setelah membuka koneksi di kode aplikasimu.

Saat WAL Bukan Pilihan yang Tepat

WAL adalah rekomendasi default, tapi ada beberapa situasi yang tidak cocok:

  • Sistem file jaringan. WAL bergantung pada shared memory (mmap) antarproses yang mengakses database. NFS, SMB, dan sejenisnya tidak mendukungnya dengan andal. Jika database-mu berada di network share, tetap pakai rollback journal, atau lebih baik lagi, jangan taruh SQLite di network share.
  • Media read-only. WAL perlu menulis file -wal dan -shm. Database di CD-ROM atau media serupa harus memakai mode journal yang tidak menulis (atau dibuka read-only dengan mode=ro).
  • Batch job dengan satu penulis tanpa pembaca serentak. WAL tidak merugikan, tapi kamu juga tidak mendapat apa-apa. Rollback journal default sudah cukup.

Untuk 95% aplikasi, yaitu backend web, aplikasi desktop, aplikasi mobile, dan perangkat tertanam dengan penyimpanan lokal, WAL adalah pilihan yang tepat.

Setup yang Realistis

Inilah bentuk yang dipakai sebagian besar setup SQLite produksi, diringkas menjadi pragma yang bisa dijalankan:

temp_store=MEMORY menyimpan tabel dan index sementara di RAM, bukan di disk. Keuntungan kecil yang gratis jika memorimu masih longgar.

Pasang ini sekali saat membuat koneksi di bagian setup database aplikasimu, dan kamu sudah menangani sebagian besar kebutuhan aplikasi berbasis SQLite agar berperilaku baik di bawah beban serentak.

Berikutnya: Backup dan Restore

Sekarang database-mu punya pendamping -wal dan -shm, menyalin file-nya bukan lagi strategi backup yang aman. Bab berikutnya membahas cara yang benar untuk melakukan backup database SQLite yang sedang berjalan: perintah .backup, online backup API, dan apa yang harus dilakukan saat kamu butuh snapshot yang konsisten tanpa mematikan aplikasi.

Pertanyaan yang sering diajukan

Apa itu mode WAL di SQLite?

WAL adalah singkatan dari write-ahead logging. Alih-alih menulis perubahan langsung ke file database utama dan memakai rollback journal untuk membatalkannya saat gagal, SQLite menambahkan perubahan ke file -wal terpisah lalu secara berkala menggabungkannya kembali. Keuntungan besarnya ada pada konkurensi: pembaca dan satu penulis bisa bekerja pada saat yang sama tanpa saling menghalangi.

Bagaimana cara mengaktifkan mode WAL di SQLite?

Jalankan PRAGMA journal_mode=WAL; sekali saja. Pengaturan ini permanen: disimpan di header file database, jadi koneksi berikutnya otomatis memakai WAL juga. Kamu tidak perlu mengaturnya di setiap koneksi. Pragma ini mengembalikan mode baru (wal) jika berhasil.

Apakah mode WAL mengizinkan penulisan serentak?

Tidak. SQLite tetap menjalankan penulisan satu per satu. Hanya satu penulis yang bisa memegang kunci tulis pada satu waktu. Yang diubah WAL adalah pembaca tidak lagi menghalangi penulis, dan penulis tidak lagi menghalangi pembaca. Untuk sebagian besar aplikasi, itulah hambatan yang penting.

Apa itu file -wal dan -shm?

File -wal berisi perubahan yang sudah di-commit tapi belum digabungkan kembali ke database utama. File -shm adalah index kecil di shared memory yang membantu koneksi menemukan halaman di dalam WAL dengan cepat. Keduanya dibuat ulang secara otomatis, tapi jika kamu menyalin database, kamu harus menyalin semuanya bersama-sama atau memakai backup API.

Ilustrasi bahasa pemrograman di Coddy

Belajar coding dengan Coddy

Mulai