Mode Journal

Journal rollback versus WAL dan pengaruhnya terhadap keandalan serta kecepatan.

Daftar isi

SQLite menyimpan bukti perubahan yang belum selesai dalam berkas samping yang disebut journal, dan pilihan bentuknya menentukan keandalan, kecepatan, serta pola konkurensi yang mungkin. Topik ini membandingkan dua mode utama, yaitu journal rollback sebagai mode bawaan dan mode WAL yang dirancang untuk aplikasi dengan banyak pembaca. Cara kerja keduanya dijelaskan lewat berkas yang benar-benar terlihat di direktori, dilanjutkan peralihan mode dengan PRAGMA journal_mode dan tabel perbandingan yang membantu memilih.

Cara kerja journal rollback#

Journal rollback bekerja dengan prinsip salin dahulu sebelum menulis. Sebelum sebuah halaman basis data dimodifikasi, salinan keadaan lamanya ditulis lebih dahulu ke berkas samping yang namanya sama dengan basis data ditambah akhiran -journal. Perubahan baru kemudian ditulis ke berkas basis data utama. COMMIT berarti seluruh perubahan dibersihkan ke perangkat penyimpanan, lalu berkas journal dihapus.

Apabila terjadi gangguan sebelum COMMIT selesai, berkas journal menjadi sumber pemulihan. Koneksi berikutnya yang membuka basis data menemukan journal yang masih ada, lalu menyalin kembali keadaan lama ke halaman-halaman yang bersangkutan sehingga tidak ada perubahan setengah jadi yang tertinggal. Mekanisme inilah yang mewujudkan atomicity pada mode bawaan, sebagaimana dipetakan pada topik transaksi.

Kehadiran berkas journal teramati langsung pada direktori kerja. Skrip berikut memulai impor massal dua ribu baris anggota dalam satu transaksi, lalu memeriksa isi direktori ketika transaksi masih terbuka. Ukuran cache halaman sengaja dibuat kecil agar sebagian halaman benar-benar tertulis ke berkas sebelum transaksi selesai.

python jurnal-rollback.py
import os
import sqlite3

DB = "demo-rollback.db"
if os.path.exists(DB):
    os.remove(DB)

def daftar():
    return sorted(n for n in os.listdir(".") if n.startswith(DB))

koneksi = sqlite3.connect(DB, isolation_level=None)
koneksi.execute("PRAGMA cache_size = 10")
koneksi.execute("CREATE TABLE anggota (id INTEGER PRIMARY KEY, nama TEXT NOT NULL)")
koneksi.execute("BEGIN")
koneksi.executemany("INSERT INTO anggota VALUES (?, ?)",
                    ((i, "Anggota %d" % i) for i in range(1, 2001)))
print("selama impor masih terbuka:")
for nama in daftar():
    print(nama)
koneksi.execute("ROLLBACK")
print("setelah rollback:")
for nama in daftar():
    print(nama)
koneksi.close()
text Keluaran
selama impor masih terbuka:
demo-rollback.db
demo-rollback.db-journal
setelah rollback:
demo-rollback.db

Berkas demo-rollback.db-journal muncul tepat ketika transaksi tulis sedang berjalan dan hilang begitu ROLLBACK selesai. Pola yang sama berlaku pada COMMIT, yaitu journal dihapus setelah seluruh perubahan diterapkan. Berkas journal dengan demikian hanya ada selama transaksi tulis berlangsung, berbeda dengan berkas samping mode WAL yang hidup lebih lama.

Peralihan ke mode WAL#

Mode WAL, singkatan dari write-ahead log, mengubah arah tulisannya. Sebagai ganti memodifikasi halaman di berkas utama, setiap COMMIT menambahkan catatan halaman baru ke akhir berkas samping bernama -wal, sedangkan berkas basis data utama dibiarkan utuh sampai proses checkpoint memindahkan catatan itu ke tempatnya. Pembaca membaca berkas utama sambil melihat -wal untuk menemukan halaman terbaru yang sudah dikomit. Pragma journal_mode menampilkan mode yang sedang aktif dan sekaligus menjadi pintu peralihannya.

bash
rm -f perpustakaan.db
sqlite3 perpustakaan.db <<'SQL'
CREATE TABLE anggota (
  id INTEGER PRIMARY KEY,
  nama TEXT NOT NULL,
  saldo_denda INTEGER NOT NULL DEFAULT 0
);
PRAGMA journal_mode;
SQL
text Keluaran
delete

Nilai kembalian delete adalah nama mode journal rollback bawaan, yang diberi nama sesuai bentuk berkas journalnya. Perintah berikut memindahkan basis data yang sama ke mode WAL lalu membaca kembali modenya.

bash
sqlite3 perpustakaan.db <<'SQL'
PRAGMA journal_mode = WAL;
PRAGMA journal_mode;
SQL
text Keluaran
wal
wal

Peralihan berhasil dan pemeriksaan kedua menegaskan mode yang aktif kini wal. Mode ini tersimpan pada kepala berkas basis data, sehingga koneksi berikutnya langsung membuka basis data dalam mode WAL tanpa perlu mengaturnya ulang. Peralihan hanya boleh dilakukan ketika tidak ada transaksi yang sedang aktif pada koneksi tersebut.

python berkas-wal.py
import os
import sqlite3

DB = "demo-wal.db"
if os.path.exists(DB):
    os.remove(DB)

def daftar():
    return sorted(n for n in os.listdir(".") if n.startswith(DB))

koneksi = sqlite3.connect(DB, isolation_level=None)
koneksi.execute("PRAGMA journal_mode = WAL")
koneksi.execute("CREATE TABLE buku (id INTEGER PRIMARY KEY, judul TEXT NOT NULL)")
koneksi.execute("INSERT INTO buku (judul) VALUES ('Anak Sungai')")
koneksi.commit()
print("selama koneksi terbuka:")
for nama in daftar():
    print(nama)
koneksi.close()
print("setelah koneksi terakhir ditutup:")
for nama in daftar():
    print(nama)
text Keluaran
selama koneksi terbuka:
demo-wal.db
demo-wal.db-shm
demo-wal.db-wal
setelah koneksi terakhir ditutup:
demo-wal.db

Selama satu koneksi masih terbuka, direktori memuat demo-wal.db-wal untuk catatan perubahan dan demo-wal.db-shm untuk indeks berbagi memori yang membantu pembaca menemukan catatan itu. Keduanya dibersihkan ketika koneksi terakhir ditutup dengan rapi, karena seluruh isinya sudah dipindahkan ke basis data utama. Berkas basis datanya sendiri tetap satu, dan tidak ada isi yang hilang oleh kepergian dua berkas samping itu.

Memilih mode journal#

Pilihan mode akhirnya berpulang pada pola akses aplikasi. Tabel berikut merangkum perbedaan kedua mode pada aspek-aspek yang paling sering menentukan.

AspekJournal rollback (DELETE)WAL
Pembaca dan penulisSaling memblokir saat COMMITTidak saling memblokir
Berkas samping-journal, hanya selama transaksi tulis-wal dan -shm, hidup selama ada koneksi
Bentuk tulisanMemodifikasi halaman di berkas utamaMenambahkan catatan di akhir -wal
Cocok untukTulis sesekali, satu proses penggunaBanyak pembaca dengan satu penulis

Mode rollback tetap tepat untuk aplikasi yang membaca sedikit dan menulis sekali-sekali, misalnya perkakas baris perintah yang memperbarui basis data lalu selesai. Mode WAL unggul pada aplikasi yang menumpuk pembaca sambil sesekali menulis, karena pembaca tidak lagi menghalangi COMMIT penulis. Tulisan pada mode WAL juga memicu operasi fsync yang lebih sedikit, sehingga cenderung lebih cepat pada perangkat yang sinkronisasinya mahal.

Satu batas lain patut diingat, yaitu seluruh proses yang berbagi basis data WAL harus berjalan pada mesin yang sama. Untuk arsip yang hampir tidak pernah ditulis, perbedaan keduanya nyaris tidak terasa, sehingga mode bawaan sudah memadai. Keputusan ini juga bukan satu untuk selamanya, karena PRAGMA journal_mode dapat dipanggil kapan pun selama tidak ada transaksi aktif.

Ringkasan#

  • Journal rollback menyalin halaman lama ke -journal sebelum menulis, lalu memulihkannya apabila terjadi gangguan sebelum COMMIT selesai.
  • Mode WAL menambahkan setiap COMMIT ke berkas -wal, sedangkan checkpoint memindahkan isinya ke basis data utama.
  • PRAGMA journal_mode menampilkan mode aktif dan mengubahnya, dan nilai yang baru tersimpan pada berkas basis data.
  • WAL menuntut seluruh proses pada mesin yang sama dan tidak berjalan pada sistem berkas jaringan.

Referensi#