Pertumbuhan data dapat membuat query yang awalnya cepat menjadi lambat, dan Optimasi Query Lambat CodeIgniter 3 menuntut tindakan langsung: jalankan profiling, cek indeks di database, dan terapkan pagination yang tidak mengandalkan offset besar. Artikel ini memandu langkah praktis untuk mengidentifikasi bottleneck dan memperbaikinya dengan tindakan yang terukur.

Profiling Query Lambat CodeIgniter 3

Profiling adalah langkah pertama untuk memahami apakah lambat terjadi di level SQL atau pada logika PHP. Aktifkan pencatatan query dan profiler bawaan agar Anda tahu query mana, durasinya, dan apakah ada operasi berulang.

Mengelola log query

Pastikan $this->db->save_queries bernilai TRUE pada konfigurasi koneksi agar CI menyimpan query terakhir dalam $this->db->queries. Di controller development, tambahkan:

$this->output->enable_profiler(TRUE);

Profiler akan memberikan tabel query beserta waktu eksekusi. Gunakan informasi ini untuk menemukan query dengan durasi tertinggi sebelum menyelam lebih dalam.

Gunakan EXPLAIN untuk rencana eksekusi

Setelah mengetahui query bermasalah, jalankan EXPLAIN langsung dari CodeIgniter:

$sql = "EXPLAIN SELECT id, title FROM articles WHERE category_id = ? ORDER BY created_at DESC";
$query = $this->db->query($sql, [$category_id]);
foreach ($query->result_array() as $row) {
    //Evaluasi tipe akses (ALL, ref, const), key yang dipakai, dan rows yang dipindai
}

Perhatikan apakah MySQL memilih "ALL" (full table scan) atau sudah memanfaatkan indeks. Tanpa EXPLAIN, Anda hanya menebak-nebak.

Memilih Indeks yang Efektif di MySQL/MariaDB

Indeks mempercepat pencarian, tetapi menambah overhead saat menulis data. Tambahkan indeks jika query sering menggunakan kolom tersebut di WHERE, JOIN, atau ORDER BY, terutama pada tabel besar.

  • Gunakan SHOW INDEX FROM nama_tabel untuk melihat indeks yang ada dan pastikan urutan kolomnya cocok dengan pattern query.
  • Kolom berjenis enum dengan kardinalitas rendah biasanya tidak efektif di-index; hindari menambahkan indeks untuk kolom status yang hanya punya dua nilai.
  • Gabungkan kolom yang digunakan bersama dalam filter berulang. Misalnya, WHERE user_id = ? AND status = ? lebih baik jika ada indeks komposit (user_id, status).

Ingat, indeks memperlama operasi INSERT/UPDATE/DELETE. Pantau ukuran indeks dan pertimbangkan maintenance rutin (analisis, optimalisasi) jika tabel sangat besar.

Pagination Efisien dengan Keyset atau Cursor

Pagination berbasis offset (LIMIT 1000, 20) membuat database harus memindai dan melewati ribuan baris sebelum mengambil hasil. Alternatifnya, gunakan pagination keyset yang bergantung pada nilai kolom terurut stabil seperti id atau timestamp.

Contoh keyset pagination di CodeIgniter 3:

$limit = 20;
$last_id = $this->input->get('after_id');
if ($last_id) {
    $this->db->where('id >', $last_id);
}
$this->db->order_by('id', 'ASC');
$this->db->limit($limit);
$query = $this->db->get('posts');

Nilai after_id bisa dikirim dari frontend bersama pagination token. Tidak ada offset besar, sehingga MySQL langsung mencari dari posisi terakhir dan menghentikan eksekusi setelah LIMIT terpenuhi.

Keyset pagination tidak cocok jika urutan bisa berubah secara cepat atau pengguna ingin melompat ke halaman tertentu (misalnya halaman 50). Dalam kasus itu, gunakan offset kecil ditambah caching untuk mengurangi frequent random access.

Memantau Bottleneck dan Transaksi

Transaksi dan jebakan locking

Transaksi dapat menyebabkan blocking jika dibiarkan terbuka terlalu lama. Pastikan setiap transaksi:

  • Berakhir dengan $this->db->trans_complete() secepatnya setelah logika selesai.
  • Dibatasi pada operasi yang memerlukan atomicity; jangan bungkus select-read yang membutuhkan banyak waktu.
  • Tidak mencampur query berat seperti full table scan di dalam transaksi karena akan menahan lock sampai selesai.

Gunakan $this->db->trans_strict(FALSE) hanya ketika Anda siap menangani rollback manual; otherwise, biarkan strict mode untuk memastikan rollback otomatis saat error.

Monitoring MySQL dan CodeIgniter

Selain profiler CI, aktifkan slow query log MySQL untuk menangkap query yang memakan waktu lebih dari threshold tertentu:

Catatan: Set waktu threshold ke 1-2 detik untuk environment pengembangan sebelum menerapkannya di produksi agar tidak menghasilkan terlalu banyak data.

Analisa slow query log bersama EXPLAIN dan indeks. Di CodeIgniter, satukan log dengan log_message('debug', $this->db->last_query()) ketika ditemukan waktu eksekusi tinggi.

Cache Ringan untuk Query Dasar

Jika query read-only dan jarang berubah, simpan hasilnya di cache sederhana agar tidak selalu menekan database. CodeIgniter 3 menyediakan driver caching file atau APC:

$cache_key = 'recent_posts_'. $category_id;
if (!$result = $this->cache->file->get($cache_key)) {
    $result = $this->db->where('category_id', $category_id)
                       ->order_by('created_at', 'DESC')
                       ->limit(20)
                       ->get('posts')
                       ->result();
    $this->cache->file->save($cache_key, $result, 300);
}

Gunakan TTL singkat (misalnya 5 menit) jika data berubah sering, dan pastikan cache dihapus saat ada update penting. Cache memperkecil jumlah query, tetapi jangan mengandalkannya untuk menjaga konsistensi penuh bila data mutakhir penting.

Dengan menerapkan profiling terukur, indeks yang relevan, pagination keyset, pemantauan transaksi/blocking, dan cache ringan, Anda dapat menjaga performa CodeIgniter 3 walau volume data terus berkembang.