Topik:
 

Apa Itu Database Indexing? Mengapa Kueri Anda Lambat

Oleh: Hobon.id (09/09/2026)
Apa Itu Database Indexing? Mengapa Kueri Anda LambatAda satu momen spesifik yang familier dan hampir pasti dialami oleh setiap developer, yaitu sebuah kueri yang berjalan instan saat pengembangan tiba-tiba memakan waktu delapan detik di lingkungan produksi; tabel diam-diam membengkak hingga dua juta baris tanpa disadari; dan dasbor yang dulunya memuat data dalam sekejap mata kini membuat pengguna harus menunggu cukup lama hingga mereka menyadarinya. Reaksi spontan yang biasanya muncul adalah menyalahkan server database, menambah perangkat keras, atau menulis ulang kueri dengan cara yang sedikit berbeda sembari berharap hal itu akan membantu. Namun, dalam sebagian besar kasus, jawaban sebenarnya jauh lebih spesifik dan solusinya jauh lebih mudah diterapkan, yaitu tabel tersebut tidak memiliki indeks yang tepat—atau bahkan tidak memiliki indeks sama sekali—sehingga database terpaksa melakukan hal yang tidak pernah dirancang untuk dilakukan secara efisien, yaitu membaca setiap baris satu per satu hanya untuk menjawab pertanyaan yang sebenarnya bisa dijawab secara instan dengan indeks yang dirancang dengan baik.

Memahami indeks database dengan benar adalah salah satu keterampilan paling berdampak besar yang bisa dipelajari oleh seorang developer, justru karena manfaat yang diperoleh sangat sepadan (bahkan jauh melampaui) upaya yang dikeluarkan. Satu indeks yang dipilih dan ditempatkan dengan tepat pada kolom yang sesuai dapat mengubah kueri yang tadinya memakan waktu beberapa detik menjadi hanya beberapa milidetik. Hal ini dicapai bukan melalui penulisan ulang kueri yang rumit ataupun penggunaan perangkat keras yang lebih canggih, melainkan dengan memberikan cara yang jauh lebih efisien bagi database untuk menemukan data yang sebenarnya dicari.
Advertisement:

Mengapa Kueri Tanpa Indeks Berjalan Sangat Lambat: Full Table Scan


Saat kita menjalankan kueri seperti "SELECT * FROM users WHERE email = "jane@example.com"" pada tabel yang tidak memiliki indeks di kolom "email", database tidak memiliki cara untuk mengetahui sebelumnya di bagian mana baris dengan alamat email tersebut disimpan. Satu-satunya pilihan adalah melakukan full table scan (pemindaian tabel secara menyeluruh), yaitu membaca setiap baris dalam tabel, satu demi satu, mulai dari halaman penyimpanan pertama hingga yang terakhir, sembari mencocokkan nilai email pada setiap baris dengan email yang kita cari. Jika tabel hanya berisi seribu baris, proses ini cukup cepat sehingga kita mungkin tidak akan menyadarinya sama sekali. Namun, jika tabel berisi dua juta baris, database harus memeriksa—rata-rata—sekitar satu juta baris sebelum menemukan kecocokan. Jika email tersebut ternyata tidak ada dalam tabel, atau jika kita mencari semua baris yang memenuhi kondisi yang lebih luas, database harus memeriksa seluruh dua juta baris tersebut tanpa kecuali, setiap kali kueri itu dijalankan.

Inilah alasan mengapa gejala klasik dari ketiadaan indeks adalah kueri yang berjalan cepat saat tahap pengembangan namun lambat saat tahap produksi: tabel dengan seratus baris uji membuat full table scan terasa hampir tanpa biaya, sedangkan kueri yang sama persis pada tabel produksi dengan jutaan baris membuat pemindaian menyeluruh tersebut menjadi sangat memakan sumber daya. Yang lebih krusial lagi, biaya pemrosesan ini terus meningkat seiring dengan bertambahnya ukuran tabel—tanpa batas—selama tabel terus membesar dan tidak ada yang menambahkan indeks yang sebenarnya dibutuhkan.


Apa Sebenarnya Indeks Itu


Indeks database adalah struktur data tambahan yang terpisah; indeks ini menyimpan salinan yang telah diurutkan—dan dapat dicari secara efisien—dari satu atau lebih kolom dalam sebuah tabel, disertai penunjuk ke lokasi baris lengkap yang bersangkutan di dalam tabel aslinya. Indeks ini ada secara khusus agar database dapat menghindari pemindaian tabel secara menyeluruh seperti yang dijelaskan sebelumnya. Indeks memberikan cara cepat dan langsung untuk melompat ke baris-baris yang benar-benar memenuhi kondisi kueri, alih-alih harus memeriksa setiap baris dalam tabel untuk menemukannya.

Analogi yang paling mudah dipahami oleh banyak orang adalah indeks di bagian belakang buku teks yang tebal dan padat informasi. Jika kita ingin menemukan setiap penyebutan kata "mitokondria" dalam buku teks biologi setebal enam ratus halaman, kita memiliki dua pilihan. Kita bisa mulai dari halaman pertama dan membaca seluruh isi buku dari awal hingga akhir sembari mencari kata tersebut—inilah yang persis dilakukan oleh pemindaian tabel menyeluruh; cara ini memang berhasil, namun sangat lambat untuk tugas yang sespesifik ini. Atau, kita bisa membuka bagian indeks di akhir buku yang mencantumkan kata "mitokondria" secara alfabetis beserta nomor halaman tempat kata itu muncul, lalu langsung menuju halaman-halaman spesifik tersebut. Indeks itu sendiri memakan ruang tambahan di dalam buku—halaman-halaman yang ada semata-mata untuk mempercepat pencarian tanpa memuat konten materi itu sendiri—dan seseorang harus melakukan pekerjaan untuk menyusun serta memeliharanya; namun, manfaat yang diperoleh siapa pun yang mencari informasi tersebut sangatlah besar. Indeks database bekerja dengan prinsip yang persis sama, hanya saja diterapkan pada kolom (bukan kata) dan pada halaman disk (bukan halaman buku).


B-Tree: Bagaimana Sebagian Besar Indeks Sebenarnya Disusun


Struktur data khusus yang digunakan oleh sebagian besar database relasional untuk mengimplementasikan indeks secara default disebut B-tree (singkatan dari balanced tree atau pohon seimbang). B-tree disusun sebagai hierarki simpul (node), dimulai dari satu simpul akar (root node) di bagian atas dan bercabang ke bawah melalui satu atau beberapa tingkat simpul internal hingga mencapai simpul daun (leaf node) di bagian paling bawah; simpul daun inilah yang sebenarnya menyimpan penunjuk ke data yang sesungguhnya. Setiap simpul dalam B-tree memuat sekumpulan nilai kunci yang telah diurutkan, dan setiap simpul internal juga memuat penunjuk ke simpul anak yang mewakili rentang nilai tertentu dari kunci-kunci tersebut. Saat database perlu menemukan nilai spesifik—misalnya, alamat email—proses dimulai dari simpul akar. Sistem membandingkan nilai yang dicari dengan kunci-kunci terurut yang tersimpan di sana, lalu mengikuti penunjuk yang sesuai untuk turun ke satu simpul anak spesifik yang mungkin memuat nilai tersebut, berdasarkan posisi nilai itu dalam urutan yang ada. Proses perbandingan dan penelusuran ke bawah ini diulangi pada setiap tingkat berikutnya, yang secara drastis mempersempit ruang pencarian di setiap langkah, hingga mencapai simpul daun yang memuat nilai yang dicari atau memastikan secara pasti bahwa nilai tersebut tidak ada sama sekali dalam tabel.

Sifat khusus yang membuat B-tree sangat efektif untuk tugas ini adalah kemampuannya untuk tetap seimbang—setiap jalur dari akar hingga ke simpul daun mana pun memiliki panjang yang sama (atau hampir sama), terlepas dari seberapa besar pertumbuhan pohon atau bagaimana distribusi datanya. Keseimbangan inilah yang menjamin performa pencarian yang konsisten dan dapat diprediksi. Berbeda dengan struktur pohon tidak seimbang yang dibuat secara asal—yang dalam skenario terburuk bisa mengalami penurunan performa hingga setara dengan pemindaian daftar biasa (pencarian linear)—B-tree yang dirancang dengan baik menjamin bahwa tabel dengan jutaan baris sekalipun dapat ditelusuri hanya dengan melewati beberapa tingkat saja—sering kali hanya tiga atau empat tingkat—sebelum menemukan jawaban yang tepat.


Mengapa B-Tree Begitu Cepat: Aspek Matematis yang Penting


Wawasan krusial yang menjelaskan mengapa indeks memberikan peningkatan performa yang sangat signifikan—bukan sekadar peningkatan kecil—terletak pada satu sifat matematis khusus, yaitu waktu pencarian pada B-tree yang seimbang meningkat secara logaritmik seiring bertambahnya jumlah baris dalam tabel, sedangkan waktu pencarian melalui pemindaian tabel penuh meningkat secara linear.

Perbedaan ini mungkin terdengar abstrak sampai kita melihat perbandingan angka nyatanya secara berdampingan. Pemindaian tabel penuh pada tabel berisi 1.000 baris akan memeriksa—dalam skenario terburuk—ke-1.000 baris tersebut. Pada tabel dengan 1.000.000 baris, pemindaian akan memeriksa seluruh 1.000.000 baris; biaya operasionalnya meningkat secara proporsional dan langsung mengikuti ukuran tabel. Sebaliknya, indeks B-tree—karena setiap tingkat pohon dapat bercabang menjadi sejumlah besar node anak (biasanya berkisar antara beberapa ratus dalam implementasi database nyata)—hanya memerlukan sedikit tambahan tingkat untuk mengakomodasi tabel yang jauh lebih besar. Pencarian pada B-tree dengan 1.000 baris mungkin hanya mengharuskan penelusuran melalui 2 tingkat. Pencarian pada tabel dengan 1.000.000 baris mungkin hanya memerlukan penelusuran melalui 4 atau 5 tingkat. Pencarian pada tabel dengan satu miliar baris mungkin hanya memerlukan 6 atau 7 tingkat. Jumlah langkah yang diperlukan bertambah sangat lambat—secara logaritmik—dibandingkan dengan laju pertumbuhan data itu sendiri; inilah alasan mengapa kueri dengan indeks yang tepat pada tabel berisi satu miliar baris tetap dapat memberikan hasil dalam hitungan milidetik, sementara kueri tanpa indeks pada tabel yang sama bisa memakan waktu hingga beberapa menit.

Inilah alasan matematis yang konkret mengapa penambahan indeks yang tepat sering disebut sebagai langkah perbaikan performa dengan dampak terbesar yang tersedia bagi developer: indeks tidak sekadar membuat database bekerja sedikit lebih keras untuk mengimbangi pertumbuhan tabel, melainkan secara fundamental mengubah pola hubungan antara ukuran tabel dan waktu kueri—dari pola yang memburuk secara proporsional seiring pertumbuhan data, menjadi pola yang hampir tidak mengalami penurunan performa sama sekali.


Indeks Clustered vs Non-Clustered


Salah satu perbedaan yang sering membingungkan bagi mereka yang baru mempelajari tentang pengindeksan—namun sangat penting untuk memahami bagaimana basis data sebenarnya menyimpan dan mengambil data—adalah perbedaan antara indeks clustered dan non-clustered.

Indeks clustered menentukan urutan fisik yang sebenarnya dari penyimpanan baris tabel di dalam disk. Karena baris-baris tersebut secara fisik hanya dapat memiliki satu urutan, sebuah tabel hanya bisa memiliki tepat satu indeks clustered—yang umumnya dibuat secara otomatis berdasarkan primary key tabel tersebut. Saat kita melakukan kueri menggunakan kunci indeks clustered, database dapat langsung menuju lokasi fisik baris yang sesuai; hal ini dimungkinkan karena leaf node pada indeks tersebut pada dasarnya adalah data tabel itu sendiri yang disusun sesuai urutan indeks, bukan berupa struktur terpisah yang mengarah ke tempat lain.

Sebaliknya, indeks non-clustered merupakan struktur yang benar-benar terpisah dari penyimpanan fisik baris tabel itu sendiri. Leaf node-nya sama sekali tidak memuat data baris yang sebenarnya, melainkan berisi penunjuk (pada sebagian besar sistem, berupa kunci indeks clustered baris tersebut atau referensi lokasi fisik langsung) yang mengarah kembali ke tempat baris lengkap itu disimpan dalam penyimpanan tabel yang terpisah. Sebuah tabel dapat memiliki banyak indeks non-clustered secara bersamaan—satu untuk setiap kolom atau kombinasi kolom yang memerlukan pencarian cepat—karena masing-masing indeks hanyalah struktur tambahan terpisah yang berada di samping data tabel sebenarnya, tanpa menentukan pengaturan fisik data itu sendiri.

Perbedaan ini memiliki konsekuensi praktis yang penting untuk dipahami, yaitu kueri yang diselesaikan sepenuhnya melalui indeks non-clustered tetap memerlukan langkah tambahan—yaitu mengikuti penunjuk kembali ke tabel utama untuk mengambil kolom apa pun yang tidak tersedia di dalam indeks itu sendiri—sebuah langkah yang umum disebut sebagai "bookmark lookup" atau "key lookup". Kueri yang dapat diselesaikan hanya dengan menggunakan kolom-kolom yang sudah ada di dalam indeks—tanpa perlu mengikuti penunjuk kembali ke baris lengkap—disebut sebagai skenario covering index; skenario ini jauh lebih cepat karena sepenuhnya melewati langkah pencarian tambahan tersebut.


Indeks Komposit dan Aturan Leftmost Prefix


Indeks tidak terbatas hanya mencakup satu kolom saja; indeks komposit (yang juga disebut indeks majemuk atau multi-column index) mencakup dua kolom atau lebih secara bersamaan. Data di dalamnya diurutkan berdasarkan kolom pertama terlebih dahulu, kemudian berdasarkan kolom kedua di dalam setiap kelompok nilai kolom pertama yang sama, dan seterusnya. Memahami cara kerja pengurutan ini sangatlah penting karena hal tersebut menentukan kueri mana yang dapat dibantu secara efektif oleh indeks komposit tertentu dan mana yang tidak.

Bayangkan sebuah indeks komposit yang dibuat berdasarkan "(last_name, first_name)". Indeks ini mengurutkan setiap baris berdasarkan last_name terlebih dahulu, dan di dalam setiap kelompok baris dengan nama belakang yang sama, data diurutkan lebih lanjut berdasarkan first_name. Struktur ini membuat indeks sangat efektif untuk kueri yang melakukan penyaringan khusus pada "last_name", atau pada kombinasi "last_name" dan "first_name" secara bersamaan; dalam kedua kasus tersebut, database dapat langsung menuju bagian yang tepat dari struktur yang telah terurut. Namun, indeks ini tidak mendukung secara efisien kueri yang hanya menyaring berdasarkan "first_name" tanpa menyertakan "last_name". Karena indeks diurutkan berdasarkan nama belakang terlebih dahulu, baris-baris dengan nama depan yang sama akan tersebar secara acak di seluruh struktur, alih-alih terkumpul di satu lokasi yang bisa langsung diakses oleh database. Mencari nama depan tertentu saja menggunakan indeks ini akan mengharuskan pemeriksaan data yang tersebar di seluruh indeks, bukan di satu bagian yang berurutan; hal ini menghilangkan sebagian besar manfaat utama yang menjadi tujuan pembuatan indeks tersebut.

Perilaku ini secara formal dikenal sebagai aturan leftmost prefix (awalan paling kiri), yaitu indeks komposit dapat mendukung secara efisien kueri yang menyaring berdasarkan kolom paling depan saja, atau berdasarkan urutan kolom yang berurutan dari depan, namun tidak mendukung kueri yang hanya menggunakan kolom di bagian belakang (kolom lanjutan) secara terpisah—dengan kata lain, kueri yang melompati kolom-kolom yang posisinya lebih awal dalam urutan indeks tersebut. Aturan ini merupakan salah satu sumber kebingungan paling umum dalam pengambilan keputusan terkait indeks di dunia nyata. Memahaminya dengan jelas adalah pembeda antara seseorang yang mampu merancang indeks komposit secara tepat sesuai kebutuhan kueri aplikasi mereka, dengan seseorang yang menambahkan indeks secara sembarangan dan kemudian bingung mengapa beberapa indeks tersebut tampaknya tidak memberikan manfaat sama sekali.


Bagaimana Query Planner Memutuskan Apakah Akan Menggunakan Indeks


Merupakan fakta yang sangat penting—dan sering kali mengejutkan—bahwa ketersediaan indeks yang relevan tidak secara otomatis menjamin database akan menggunakannya untuk kueri tertentu. Setiap database relasional modern memiliki komponen yang disebut query planner (atau query optimizer) yang menganalisis kueri, mempertimbangkan indeks yang tersedia serta statistik yang dikelola database mengenai data tabel itu sendiri, lalu memutuskan strategi yang diperkirakan paling efisien untuk mengeksekusi kueri tersebut. Terkadang, keputusan yang tepat justru berupa pemindaian tabel secara menyeluruh, meskipun secara teknis terdapat indeks yang relevan.

Hal ini paling sering terjadi ketika kondisi filter kueri mencakup proporsi yang sangat besar dari total baris tabel, sehingga penggunaan indeks justru akan lebih lambat secara keseluruhan dibandingkan memindai tabel secara langsung. Jika kolom boolean "is_active" bernilai true pada 95 persen baris tabel, indeks pada kolom tersebut tidak memberikan banyak manfaat nyata untuk kueri yang memfilter "is_active = true". Alasannya, database tetap harus mengambil sebagian besar baris tabel apa pun strategi pencarian yang digunakan; sementara itu, beban tambahan untuk menavigasi struktur indeks dan mengikuti pointer menuju baris-baris tersebut satu per satu bisa jadi lebih besar daripada biaya membaca tabel secara berurutan dan memeriksa setiap baris secara langsung. Query planner membuat keputusan ini berdasarkan statistik mengenai distribusi nilai aktual dalam kolom tabel. Oleh karena itu, statistik tersebut harus senantiasa diperbarui (melalui operasi seperti "ANALYZE" di PostgreSQL atau pembaruan statistik di SQL Server); statistik yang sudah usang dapat menyebabkan planner mengambil keputusan yang kurang tepat atau tidak relevan mengenai strategi mana yang paling efektif untuk data tabel yang sebenarnya saat ini.


Membaca Execution Plan untuk Mendiagnosis Kueri yang Lambat


Daripada menebak-nebak apakah suatu indeks benar-benar digunakan, setiap database relasional utama menyediakan cara untuk menanyakan langsung strategi apa yang akan atau telah digunakan untuk kueri tertentu. Hal ini umumnya dilakukan melalui perintah seperti "EXPLAIN" atau "EXPLAIN ANALYZE" di PostgreSQL dan MySQL, atau dengan melihat execution plan (rencana eksekusi) grafis di SQL Server Management Studio. Mempelajari cara membaca hasil ini merupakan keterampilan diagnostik paling berharga bagi siapa pun yang ingin mempercepat kueri lambat yang nyata, karena metode ini menggantikan dugaan dengan jawaban langsung dan konkret dari database itu sendiri.

Execution plan untuk kueri yang lambat biasanya akan menampilkan langkah yang secara eksplisit diberi label seperti "Seq Scan" (pemindaian tabel secara penuh dan berurutan) pada tabel terkait. Ini sering kali menjadi indikasi paling jelas bahwa kueri tersebut tidak memanfaatkan indeks sama sekali, melainkan membaca seluruh isi tabel baris demi baris. Sebaliknya, rencana yang lebih efisien untuk kueri yang mampu memanfaatkan indeks secara efektif akan menampilkan langkah berlabel seperti "Index Scan" atau "Index Seek". Label ini menunjukkan bahwa database menavigasi langsung ke baris yang relevan melalui struktur indeks, alih-alih membaca seluruh data. Execution plan juga biasanya melaporkan estimasi jumlah baris dan biaya untuk setiap langkah—serta angka aktualnya saat kueri benar-benar dijalankan. Informasi ini sangat berguna untuk mendeteksi kasus di mana estimasi perencana ternyata sangat keliru dibandingkan dengan kenyataan di lapangan; hal ini merupakan tanda kuat dan spesifik bahwa statistik tabel sudah usang dan perlu diperbarui.

Mempelajari cara membaca struktur dasar execution plan—khususnya memeriksa apakah rencana kueri lambat menunjukkan pemindaian sekuensial pada tabel besar padahal seharusnya terjadi pemindaian indeks —mengubah proses debugging performa. Aktivitas yang tadinya hanya berupa upaya coba-coba yang membuat frustrasi kini berubah menjadi proses yang langsung dan berbasis bukti nyata.


Biaya Nyata dari Indeks: Mengapa Kita Tidak Sebaiknya Mengindeks Segalanya


Setiap indeks pada sebuah tabel harus dikelola secara aktif dan disinkronkan setiap kali data dasarnya berubah. Saat baris baru disisipkan, database tidak hanya menulis baris tersebut ke dalam tabel, tetapi juga harus menyisipkan entri baru yang sesuai ke dalam setiap indeks yang didefinisikan pada tabel tersebut. Entri ini harus ditempatkan pada posisi urutan yang benar di setiap indeks, yang dalam beberapa kasus dapat mengharuskan penyeimbangan ulang struktur B-tree indeks itu sendiri agar tetap seimbang. Beban kerja tambahan serupa juga berlaku untuk setiap operasi update yang melibatkan kolom terindeks, serta untuk setiap operasi delete. Sebuah tabel dengan sepuluh indeks terpisah menanggung biaya pemeliharaan ini sepuluh kali lipat untuk setiap operasi penulisan; akibatnya, tabel dengan banyak indeks dan frekuensi penulisan tinggi dapat mengalami penurunan kinerja penyisipan, pembaruan, dan penghapusan yang signifikan, meskipun kinerja pembacaan datanya justru meningkat.

Indeks juga memakan ruang penyimpanan nyata yang berkelanjutan—terkadang dalam jumlah yang sangat besar, terutama untuk indeks komposit yang mencakup beberapa kolom atau indeks pada tabel dengan jumlah baris yang sangat banyak. Dalam kasus tersebut, ukuran indeks itu sendiri bisa menjadi bagian yang cukup besar dibandingkan ukuran data tabel aslinya. Selain itu, banyaknya indeks memberikan lebih banyak opsi bagi query planner untuk dipertimbangkan dan dievaluasi saat menentukan strategi eksekusi. Pada kasus-kasus ekstrem tertentu, hal ini bahkan dapat memperlambat proses perencanaan kueri itu sendiri, meskipun dalam praktiknya dampak ini biasanya jauh lebih kecil kekhawatirannya dibandingkan dengan masalah kinerja penulisan dan biaya penyimpanan yang telah disebutkan sebelumnya.

Kesimpulan praktisnya adalah bahwa pengindeksan melibatkan pertukaran nyata, bukan sekadar peningkatan satu arah: pendekatan yang tepat adalah mengindeks secara spesifik kolom-kolom yang benar-benar sering digunakan dalam klausa "WHERE", kondisi "JOIN", dan klausa "ORDER BY" untuk kueri yang secara rutin dijalankan oleh aplikasi tersebut—bukan mengindeks setiap kolom hanya karena hal itu secara teknis dimungkinkan.


Alasan Umum Mengapa Indeks Tidak Digunakan Sebagaimana Mestinya Tanpa Notifikasi


Selain keputusan sengaja dari query planner untuk tidak menggunakan indeks yang tersedia (seperti yang telah dibahas sebelumnya), ada beberapa pola umum lain yang dapat mencegah penggunaan indeks sama sekali tanpa adanya peringatan eksplisit. Hal ini terjadi meskipun indeks tersebut ada dan—secara prinsip—tampak relevan dengan query yang dijalankan. Memahami hal ini sangatlah penting karena pola-pola tersebut sering menjadi penyebab utama kebingungan developer yang bertanya-tanya, "Mengapa kueri masih lambat padahal saya sudah membuat indeks?"

Menerapkan fungsi pada kolom yang memiliki indeks di dalam klausa "WHERE"—misalnya, "WHERE LOWER(email) = "jane@example.com"" pada indeks standar yang dibuat untuk kolom email asli tanpa modifikasi—umumnya mencegah database menggunakan indeks tersebut sama sekali. Alasannya adalah indeks tersebut dibuat untuk mencari nilai kolom asli secara efisien, bukan untuk mencari hasil transformasi dari fungsi yang diterapkan pada nilai tersebut. Database harus menerapkan fungsi yang sama pada nilai setiap baris sebelum dapat membandingkannya dengan nilai target kita; proses ini justru meniadakan tujuan utama dari struktur indeks yang terurut dan dapat dicari secara langsung. Solusinya, jika pola ini memang diperlukan, biasanya berupa pembuatan functional index khusus untuk ekspresi hasil transformasi tersebut (fitur yang didukung oleh beberapa database utama) atau perubahan pada tingkat skema, seperti menyimpan versi kolom yang sudah dinormalisasi atau diubah menjadi huruf kecil (lowercase) sejak awal.

Penggunaan wildcard di awal pola "LIKE"—seperti "WHERE name LIKE "%smith"", berbeda dengan "WHERE name LIKE "smith%""—juga mencegah penggunaan indeks B-tree standar secara efisien. Hal ini dikarenakan indeks B-tree mengurutkan data berdasarkan karakter pertamanya, sehingga pola pencarian yang bisa cocok di posisi mana pun selain awal string tidak dapat ditemukan secara efisien menggunakan urutan tersebut. Sebaliknya, penggunaan wildcard di akhir pola (trailing wildcard) dapat bekerja dengan baik bersama indeks standar karena pencarian tersebut dapat diperlakukan sebagai rentang data terurut yang dimulai dari prefiks yang ditentukan. Ketidakcocokan tipe data yang signifikan antara tipe penyimpanan aktual kolom dan nilai yang dibandingkan dengannya dalam sebuah kueri—misalnya, membandingkan kolom numerik dengan nilai yang masuk sebagai string—dapat memicu konversi tipe implisit pada sistem database tertentu; hal ini juga menghalangi penggunaan indeks secara optimal, meskipun perilaku spesifiknya sangat bervariasi tergantung pada sistem dan versi database yang digunakan.


Proses Praktis untuk Mendiagnosis dan Memperbaiki Kueri yang Lambat


Mulailah dengan menjalankan kueri menggunakan "EXPLAIN ANALYZE" (atau perintah serupa pada database kita) untuk melihat apa yang sebenarnya dilakukan oleh database, bukan sekadar berasumsi berdasarkan teks kueri itu sendiri. Perhatikan secara khusus adanya sequential scan atau full table scan pada tabel yang—mengingat kondisi filter kueri tersebut—seharusnya bisa memanfaatkan indeks; ini adalah penyebab utama yang paling sering ditemui pada kueri yang lambat secara tak terduga. Jika kita menemukannya, periksa apakah indeks yang relevan benar-benar ada pada kolom-kolom yang terlibat dalam klausa "WHERE", "JOIN", atau "ORDER BY" kueri tersebut; jika tidak ada, kemungkinan besar itulah solusi langsung yang kita butuhkan. Jika indeks yang relevan sudah ada namun tampaknya tidak digunakan, periksa pola-pola umum yang menghambat penggunaan indeks sebagaimana dijelaskan sebelumnya—seperti fungsi yang diterapkan langsung pada kolom berindeks, wildcard di awal pola "LIKE", atau ketidakcocokan tipe data—dan pertimbangkan juga secara khusus apakah statistik dasar tabel tersebut mungkin sudah usang dan perlu diperbarui. Selain itu, jika kita membuat indeks baru untuk mempercepat kueri tertentu yang lambat, tentukan urutan kolom secara cermat dan spesifik untuk indeks komposit apa pun; ikuti aturan leftmost prefix yang telah dijelaskan sebelumnya dan sesuaikan urutan kolom indeks dengan kondisi filter aktual yang benar-benar digunakan oleh kueri yang ingin kita perbaiki.

Selama proses ini berlangsung, tahanlah godaan yang wajar untuk sekadar menambahkan beberapa indeks sekaligus dengan harapan salah satunya akan membantu; tambahkanlah satu indeks pada satu waktu, jalankan ulang rencana eksekusi untuk memastikan indeks tersebut benar-benar digunakan sesuai harapan, serta pertahankan pemahaman yang jelas dan spesifik mengenai kueri mana yang didukung oleh setiap indeks dalam skema kita, alih-alih menumpuk indeks yang tujuan awalnya tidak dapat dijelaskan secara jelas oleh siapa pun di tim setelah beberapa bulan berlalu.
Advertisement:
Jadi, pada dasarnya, pengindeksan database adalah gagasan yang sangat sederhana namun benar-benar ampuh: alih-alih memaksa database memeriksa setiap baris untuk menjawab suatu pertanyaan, berikanlah struktur terpisah yang terurut secara efisien agar sistem dapat menavigasi secara langsung dan cepat menuju baris-baris yang memang relevan. Struktur B-tree, yang menerapkan gagasan ini di sebagian besar database, mencapai keunggulan performa yang signifikan berkat sifat matematis nyata yang spesifik—yaitu pertumbuhan waktu pencarian yang bersifat logaritmik, bukan linear. Inilah alasan mengapa indeks yang tepat dapat mengubah kueri yang tadinya memakan waktu beberapa detik menjadi hanya beberapa milidetik, serta mengapa kesenjangan performa tersebut justru semakin melebar seiring bertambahnya jumlah baris dalam tabel dari waktu ke waktu.
Artikel Terkait: