Satu Index, Beberapa Kolom
Composite index, kadang disebut index multi kolom, adalah satu index yang dibangun di atas dua kolom atau lebih. Kamu membuatnya dengan menuliskan kolom-kolomnya secara berurutan:
Index idx_orders_customer_status menyimpan entri dengan urutan berdasarkan customer_id lebih dulu, lalu berdasarkan status di dalam setiap customer. Urutan itulah intinya: semua hal lain tentang composite index berasal dari situ.
Gambaran Sederhananya: Buku Telepon yang Terurut
Bayangkan buku telepon zaman dulu. Entrinya diurutkan berdasarkan nama belakang, dan di dalam setiap nama belakang, berdasarkan nama depan. Seperti itulah tampilan index pada (last_name, first_name).
Ada pencarian yang murah, ada yang tidak:
- "Cari semua orang bernama Patel": mudah, semua Patel berada berdekatan.
- "Cari Priya Patel": mudah, lompat ke Patel, lalu telusuri sampai Priya.
- "Cari semua orang bernama Priya": lambat, kamu harus menelusuri setiap halaman. Priya tersebar di semua nama belakang.
Composite index di SQLite bekerja dengan cara yang sama. Kolom pertama adalah key urutan utama, dan kolom kedua hanya mengurutkan entri yang punya nilai kolom pertama yang sama.
Aturan Leftmost Prefix
SQLite hanya bisa memakai composite index untuk sebuah query kalau klausa WHERE membatasi leftmost prefix dari kolom-kolomnya, yaitu kolom-kolom paling kiri secara berurutan. Untuk index pada (a, b, c):
- Memfilter
a: index dipakai. - Memfilter
adanb: index dipakai. - Memfilter
a,b, danc: index dipakai. - Memfilter
bsaja,csaja, ataubdanc: index tidak dipakai.
Kamu bisa memastikannya langsung dengan EXPLAIN QUERY PLAN:
Rencana pertama melaporkan SEARCH events USING INDEX idx_events_user_kind_time. Rencana kedua kembali ke SCAN events: memfilter kind saja melewati kolom user_id di urutan pertama, jadi index-nya tidak berguna untuk query itu.
Urutan Kolom Adalah Keputusan Desain
Karena leftmost prefix itu penting, urutan kolom yang kamu tulis di CREATE INDEX adalah pilihan nyata, bukan soal gaya. Ada dua patokan praktis:
- Taruh kolom yang paling sering kamu filter di urutan pertama. Kolom itu membuka pemakaian index untuk rentang query yang paling luas.
- Taruh kolom kesamaan sebelum kolom rentang. SQLite bisa menelusuri index memakai
=, lalu memindai rentang yang berurutan memakai<,>, atauBETWEEN, tapi hanya pada kolom terakhir yang dipakai.
Rencananya menampilkan SEARCH sales USING INDEX idx_sales_region_time (region=? AND sold_at>?). SQLite langsung melompat ke region = 'EU', lalu bergerak maju melewati rentang tanggal. Kalau urutan kolomnya dibalik menjadi (sold_at, region), query yang sama harus memindai setiap baris dalam rentang tanggal itu dan memeriksa ulang region di setiap baris.
Composite vs Beberapa Index Satu Kolom
Pertanyaan yang sering muncul: sebaiknya saya membuat satu index pada (a, b), atau dua index terpisah pada a dan b?
Untuk filter gabungan, composite index lebih cepat: SQLite langsung menuju entri (project_id, state) yang cocok. Dengan dua index satu kolom, SQLite biasanya memilih salah satu, memakainya untuk mempersempit baris, lalu memeriksa ulang kolom yang lain di setiap baris yang cocok. Kadang SQLite bisa memotongkan keduanya, tapi composite index adalah jawaban yang lebih rapi kalau kolom-kolom itu di-query bersamaan.
Kalau project_id dan state juga di-query secara mandiri, mungkin kamu butuh keduanya: composite index untuk filter gabungan, ditambah index satu kolom pada state untuk query yang hanya memfilter kolom itu.
Covering Index
Kalau sebuah index berisi setiap kolom yang dibutuhkan query, baik kolom filter maupun kolom yang dipilih, SQLite bisa menjawab query tanpa menyentuh tabel sama sekali. Itulah covering index, dan itu adalah kecepatan tertinggi yang bisa dicapai sebuah query.
Rencananya menampilkan USING COVERING INDEX idx_invoices_cover. Query membaca issued_at dan total langsung dari index. notes dan id tidak dibutuhkan, jadi tabelnya sendiri tidak pernah dibuka. Menambahkan kolom ke composite index semata-mata untuk mencakup query yang sering dijalankan adalah pertukaran yang layak kalau query itu dijalankan terus-menerus.
Constraint UNIQUE Komposit
Composite index juga bisa menegakkan keunikan pada kombinasi kolom. Ini berguna kalau tidak ada satu kolom pun yang unik dengan sendirinya, tapi kombinasinya harus unik:
Insert ketiga memunculkan UNIQUE constraint failed: enrollments.student_id, enrollments.course_id. Pasangan yang sama sudah ada di index, jadi SQLite menolak duplikatnya.
Jebakan yang Perlu Diketahui
ORdi antara kolom yang bukan kolom pertama menghalangi index.WHERE a = 1 OR b = 2pada index(a, b)biasanya sama sekali tidak bisa memakai index, karena SQLite perlu mempertimbangkan kedua cabang secara terpisah.- Fungsi pada kolom yang di-index menonaktifkan index.
WHERE lower(email) = 'x'tidak akan memakai index padaemail. Buat index pada ekspresinya, atau normalisasi data saat insert. - Index tidak gratis. Setiap index diperbarui pada setiap
INSERT,UPDATE(pada kolom yang di-index), danDELETE. Tiga composite index pada tabel yang sering ditulisi bisa mendominasi biaya penulisan. - Jalankan
ANALYZEsetelah membuat index. Planner SQLite memakai statistik yang dikumpulkanANALYZEuntuk memilih di antara index yang tersedia. Tanpa statistik itu, ia kembali ke aturan perkiraan yang tidak selalu optimal.
Alur Kerja yang Praktis
Saat menyetel query yang lambat, putarannya biasanya seperti ini:
- Jalankan
EXPLAIN QUERY PLANpada query untuk melihat apa yang dilakukan SQLite saat ini. - Kalau ia memindai, lihat klausa
WHERE: kolom mana yang memakai kesamaan? Kolom mana yang memakai rentang? Apa yang dipilih? - Buat composite index dengan urutan kolom kesamaan dulu, kolom rentang kedua, lalu tambahkan kolom yang dipilih kalau covering membantu.
- Jalankan
ANALYZE. - Jalankan
EXPLAIN QUERY PLANlagi. Pastikan rencananya berubah dan index-nya dipakai. - Ukur waktu query sebelum dan sesudah pada data yang representatif.
Kalau kamu melewatkan langkah 6, tanggung sendiri risikonya. Index yang terlihat benar di rencana query tetap bisa lebih lambat dalam praktik kalau tabelnya kecil atau planner memilih jalur lain.
Berikutnya: Partial Index
Composite index mencakup setiap baris di tabel. Tapi sering kali, hanya sebagian kecil baris yang penting: tiket yang masih terbuka, job yang belum diproses, record yang belum dihapus. Partial index memungkinkanmu membuat index hanya untuk baris-baris itu, dengan klausa WHERE yang tertanam di index itu sendiri. Itu halaman berikutnya.
Pertanyaan yang sering diajukan
Apa itu composite index di SQLite?
Composite index adalah satu index yang mencakup dua kolom atau lebih. Kamu membuatnya dengan CREATE INDEX idx_name ON table(col_a, col_b). SQLite menyimpan entrinya dengan urutan berdasarkan col_a lebih dulu, lalu berdasarkan col_b di dalam setiap nilai col_a, seperti buku telepon yang diurutkan berdasarkan nama belakang, lalu nama depan.
Apakah urutan kolom penting dalam composite index SQLite?
Ya, sangat penting. SQLite hanya bisa memakai composite index untuk sebuah query kalau klausa WHERE memfilter leftmost prefix dari kolom-kolom yang di-index. Index pada (a, b, c) membantu query yang memfilter a, a dan b, atau ketiganya, tapi tidak bisa membantu query yang hanya memfilter b atau hanya c.
Kapan sebaiknya memakai composite index alih-alih beberapa index satu kolom?
Pakai composite index kalau query sering memfilter atau mengurutkan kombinasi kolom yang sama secara bersamaan. Index satu kolom yang terpisah cocok kalau setiap kolom di-query secara mandiri. Jalankan EXPLAIN QUERY PLAN untuk melihat index mana yang benar-benar dipilih SQLite, karena hanya itu umpan balik yang bisa diandalkan.
Apa itu covering index di SQLite?
Covering index berisi setiap kolom yang dibutuhkan query, sehingga SQLite bisa menjawab query langsung dari index tanpa menyentuh tabel. EXPLAIN QUERY PLAN menampilkan USING COVERING INDEX kalau ini terjadi. Menambahkan kolom ekstra ke composite index hanya untuk mencakup query yang sering dijalankan adalah optimasi yang umum.