Janji utama SQLite — satu database dalam satu berkas — baru terasa nyaman ketika kita tahu apa isi berkas itu. Format berkas ini terdokumentasi lengkap, stabil antarversi, dan lintas platform, sehingga memahami strukturnya membantu kita memakai backup, pemulihan, dan fitur lain dengan yakin. Pada chapter ini kita membedah berkas sungguhan yang kita buat sendiri.

Database adalah Rangkaian Halaman #

Sebuah berkas database SQLite adalah rangkaian halaman (page) berukuran sama. Ukuran halaman selalu pangkat dua antara 512 hingga 65536 byte; ukuran bawaannya 4096 byte. Halaman diberi nomor mulai dari 1, dan setiap halaman memiliki tepat satu peran: halaman b-tree milik sebuah table atau index, halaman freelist penampung ruang kosong, atau halaman overflow untuk data yang tidak muat di satu halaman.

Halaman pertama istimewa. Di dalamnya berbaris 100 byte pertama berupa header database, disusul akar b-tree dari table skema sqlite_master — table internal yang mencatat seluruh objek (table, index, view, trigger) milik database. Susunan berkas kecil yang kita buat di bawah dapat digambarkan begini:

Membedah Berkas Sungguhan #

Kita buat database kecil berisi satu table tiga baris, lalu memeriksa berkasnya dari luar:

bash
/home/z/my-project/tools/sqlite3 belanja.db "CREATE TABLE barang (id INTEGER PRIMARY KEY, nama TEXT, harga INTEGER); INSERT INTO barang (nama, harga) VALUES ('Kopi Arabika', 45000), ('Teh Hijau', 15000), ('Gula Aren', 25000);"
ls -l belanja.db

Output yang Dihasilkan:

text
-rw-r--r-- 1 z z 8192 Oct 13 03:19 belanja.db

Berkas berukuran 8192 byte — tepat dua halaman berukuran 4096 byte. CLI juga bisa membaca header berkas itu langsung lewat perintah .dbinfo:

bash
/home/z/my-project/tools/sqlite3 belanja.db ".dbinfo"

Output yang Dihasilkan:

text
database page size:  4096
write format:        1
read format:         1
reserved bytes:      0
file change counter: 2
database page count: 2
freelist page count: 0
schema cookie:       1
schema format:       4
default cache size:  0
autovacuum top root: 0
incremental vacuum:  0
text encoding:       1 (utf8)
user version:        0
application id:      0
software version:    3050001
number of tables:    1
number of indexes:   0
number of triggers:  0
number of views:     0
schema size:         70
data version         1

Dua angka itu bisa kita pastikan lewat SQL biasa:

bash
/home/z/my-project/tools/sqlite3 belanja.db "PRAGMA page_size; PRAGMA page_count;"

Output yang Dihasilkan:

text
4096
2

Isi skema database sendiri dapat dibaca seperti table biasa:

bash
/home/z/my-project/tools/sqlite3 belanja.db "SELECT type, name FROM sqlite_master ORDER BY name;"

Output yang Dihasilkan:

text
table|barang

Aritmetikanya cocok: dua halaman × 4096 byte = 8192 byte. Halaman 1 menampung header beserta skema, halaman 2 menampung b-tree table barang beserta tiga barisnya. Ketika data bertambah dan halaman 2 penuh, engine SQLite otomatis menambah halaman baru dan berkas membesar kelipatan ukuran halaman.

Magic String di 16 Byte Pertama #

Setiap berkas database SQLite 3 diawali string ajaib SQLite format 3\0. Kita bisa melihatnya langsung dengan od:

bash
od -c belanja.db | head -2

Output yang Dihasilkan:

text
0000000   S   Q   L   i   t   e       f   o   r   m   a   t       3  \0
0000020 020  \0 001 001  \0   @          \0  \0  \0 002  \0  \0  \0 002

Baris pertama adalah 16 byte pertama: teks SQLite format 3 yang diakhiri byte kosong. Baris kedua dimulai pada offset 16: dua byte 020 \0 (oktal) adalah ukuran halaman 0x1000 = 4096, disusul byte write format dan read format, lalu @ dan dua spasi — byte 64, 32, 32 yang wajib ada pada format ini. Dengan begitu banyak informasi yang bisa dibaca oleh alat umum semacam od, wajar jika berkas SQLite disebut self-describing.

Berkas Pendamping: Journal dan WAL #

Selama transaction berjalan, SQLite menulis informasi pemulihan ke berkas pendamping di samping berkas utama: belanja.db-journal pada mode rollback journal bawaan, atau belanja.db-wal dan belanja.db-shm pada mode WAL. Berkas pendamping inilah yang menyelamatkan database dari crash: bila proses mati sebelum COMMIT, jurnal dipakai untuk mengembalikan berkas utama ke keadaan konsisten.

Buktinya mudah ditangkap. Saat ada proses lain yang sedang menahan transaction terbuka — BEGIN dan INSERT sudah dijalankan, COMMIT belum — isi direktori tampak seperti ini:

bash
ls -l belanja.db*

Output yang Dihasilkan:

text
-rw-r--r-- 1 z z 8192 Oct 13 03:19 belanja.db
-rw-r--r-- 1 z z 4616 Oct 13 03:19 belanja.db-journal

Begitu proses tersebut menyelesaikan COMMIT dan keluar dengan bersih, berkas jurnal lenyap dan hanya berkas utama tersisa — pemulihan tidak lagi diperlukan. Mekanisme inilah yang kita lihat dari luar sebagai jaminan ACID pada Chapter Apa itu SQLite?

Format yang Stabil dan Lintas Platform #

Format berkas ini dipakai oleh seluruh rilis SQLite 3 sejak versi 3.0.0 (2004) dan dijamin tetap kompatibel ke depan. Semua angka multibyte di header disimpan big-endian, sehingga berkas yang dibuat di mesin little-endian tetap terbaca di mesin big-endian dan sebaliknya — inilah dasar klaim lintas platform pada bab-bab sebelumnya. Batas ukurannya sendiri sangat longgar: satu berkas database dapat menampung hingga sekitar 281 terabyte.

Dengan pemahaman ini, fondasi konsep SQLite kita sudah lengkap. Langkah berikutnya adalah menyiapkan peralatan kerja pada Section Instalasi dan Alat-Alat.