Apa yang sebenarnya terjadi ketika kita mengetik SELECT * FROM barang? Memahami perjalanan sebuah query di balik layar membuat keputusan optimasi dan pesan error yang kita temui nanti jauh lebih masuk akal. Untungnya arsitektur SQLite terkenal bersih dan modular — seluruh alurnya cukup digambar dalam satu diagram.

Peta Besar Arsitektur #

Dokumentasi resmi menggambarkan SQLite sebagai tumpukan komponen berikut:

Alurnya searah dari atas ke bawah: teks SQL masuk lewat interface, dikompilasi menjadi bytecode, dieksekusi oleh virtual machine, lalu lapisan backend menyentuh disk melalui abstraksi sistem operasi. Kita bedah tiap lapisan satu per satu.

Dari Teks SQL ke Bytecode #

Tiga komponen pertama bekerja layaknya sebuah compiler:

  1. Tokenizer memecah teks SQL menjadi token: kata kunci SELECT, nama barang, operator *, dan seterusnya.
  2. Parser memberi makna pada urutan token sesuai tata bahasa SQL dan membentuk parse tree. Parser SQLite dihasilkan oleh generator Lemon, bukan oleh YACC/BISON.
  3. Code generator menganalisis parse tree lalu membangkitkan bytecode untuk virtual machine. Di tahap ini pula query planner bekerja: komponen yang memilih algoritma tercepat di antara banyak cara menjalankan query yang sama — pakai index atau full scan, urutan join mana, dan sebagainya.

Hasil akhirnya: setiap statement SQL menjadi program kecil untuk sebuah virtual machine bernama VDBE. VDBE mengeksekusi bytecode itu seperti CPU mengeksekusi instruksi mesin — satu per satu, dengan register dan cursor miliknya sendiri. Desain ini menjelaskan mengapa API SQLite memisahkan sqlite3_prepare() yang mengompilasi SQL menjadi bytecode, dan sqlite3_step() yang menjalankan bytecode itu hingga satu baris hasil siap diserahkan.

Bytecode tersebut bahkan bisa kita saksikan langsung dengan EXPLAIN:

bash
/home/z/my-project/tools/sqlite3 :memory: "CREATE TABLE nilai (id INTEGER PRIMARY KEY, siswa TEXT, skor INTEGER); EXPLAIN SELECT siswa FROM nilai WHERE skor > 75;"

Output yang Dihasilkan:

text
addr  opcode         p1    p2    p3    p4             p5  comment      
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     9     0                    0   Start at 9
1     OpenRead       0     2     0     3              0   root=2 iDb=0; nilai
2     Rewind         0     8     0                    0   
3       Column         0     2     1                    0   r[1]= cursor 0 column 2
4       Le             2     7     1     BINARY-8       84  if r[1]<=r[2] goto 7
5       Column         0     1     3                    0   r[3]= cursor 0 column 1
6       ResultRow      3     1     0                    0   output=r[3]
7     Next           0     3     0                    1   
8     Halt           0     0     0                    0   
9     Transaction    0     0     1     0              1   usesStmtJournal=0
10    Integer        75    2     0                    0   r[2]=75
11    Goto           0     1     0                    0   

Beberapa opcode penting dari program di atas:

  1. Init dan Goto: eksekusi dimulai dengan melompat ke addr 9 untuk menyiapkan transaction dan register r[2] berisi 75, lalu kembali ke addr 1.
  2. OpenRead: membuka b-tree milik table nilai yang berakar di halaman 2 (halaman 1 sudah dipakai table skema, sqlite_master).
  3. Rewind dan Next: membaca table baris demi baris — full scan, karena belum ada index yang bisa dimanfaatkan.
  4. Le: membandingkan skor dengan 75; baris dengan skor 75 ke bawah dilompati ke Next.
  5. ResultRow: menyerahkan satu baris hasil (kolom siswa) kepada pemanggil query.

Query sederhana itu benar-benar berupa program 12 instruksi — dan query yang lebih rumit menghasilkan program yang lebih panjang, dengan struktur yang sama.

Backend: B-Tree, Pager, dan VFS #

Tiga lapisan terbawah menangani penyimpanan:

  • B-Tree: setiap table dan setiap index disimpan sebagai struktur b-tree tersendiri, dan semuanya hidup di satu berkas yang sama. Table b-tree menjaga baris terurut berdasarkan rowid sehingga pencarian berdasarkan key menjadi cepat.
  • Pager: lapisan yang membaca dan menulis disk dalam satuan halaman (page) berukuran tetap — bawaannya 4096 byte, dapat diatur ke pangkat dua antara 512 hingga 65536 byte. Pager juga mengelola cache halaman di memori, locking berkas, serta mekanisme rollback dan atomic commit; inilah penjaga jaminan ACID SQLite.
  • OS Interface (VFS): abstraksi panggilan sistem operasi — buka berkas, baca, tulis, kunci. Berkat VFS, kode yang sama dapat dikompilasi di Unix, Windows, hingga sistem tertanam tanpa sistem operasi.

Pemisahan tanggung jawab yang rapi inilah yang membuat SQLite mudah dipelajari dan dimodifikasi; kode sumbernya kerap dipakai sebagai bahan ajar cara membangun database engine.

Bagaimana halaman-halaman itu tersusun di dalam berkas database adalah materi Chapter Format Berkas Database.