Database Query Performance — Performance

Database Query Performance Performa database sering menjadi bottleneck utama aplikasi web. Query yang lambat bisa membuat halaman yang secepat apapun terasa lem

Database Query Performance

Performa database sering menjadi bottleneck utama aplikasi web. Query yang lambat bisa membuat halaman yang secepat apapun terasa lemot.

Masalah N+1 Query

Ini adalah masalah paling umum dan paling merusak performa:

// BURUK — N+1 problem: 1 query untuk ambil posts,
// lalu 1 query PER post untuk ambil author
const posts = await db.query("SELECT * FROM posts"); // Query 1

for (const post of posts) {
  // Query 2, 3, 4... N+1!
  const author = await db.query(
    "SELECT * FROM users WHERE id = ?", [post.author_id]
  );
  post.author = author;
}

// BAIK — 1 query dengan JOIN
const posts = await db.query(`
  SELECT posts.*, users.name AS author_name
  FROM posts
  JOIN users ON users.id = posts.author_id
`);

Index — Kunci Performa Query

Index seperti daftar isi buku — daripada baca semua halaman, langsung ke halaman yang tepat.

-- Tanpa index: full table scan (baca semua row)
SELECT * FROM orders WHERE user_id = 123;
-- → Baca 1.000.000 row untuk cari 5 order

-- Dengan index: langsung ke row yang tepat
CREATE INDEX idx_orders_user_id ON orders(user_id);
SELECT * FROM orders WHERE user_id = 123;
-- → Baca 5 row langsung

EXPLAIN — Analisis Query

EXPLAIN SELECT * FROM orders
WHERE user_id = 123
ORDER BY created_at DESC;

-- Output yang perlu diperhatikan:
-- type: ALL (bad) vs ref/eq_ref (good)
-- rows: estimasi jumlah row yang dibaca
-- key: index yang digunakan (NULL = tidak ada)
-- Extra: "Using filesort" atau "Using temporary" = warning

Query Anti-patterns

-- BURUK: SELECT * mengambil semua kolom
SELECT * FROM users;

-- BAIK: Ambil kolom yang diperlukan saja
SELECT id, name, email FROM users;

-- BURUK: Fungsi di WHERE clause → tidak bisa pakai index
SELECT * FROM orders WHERE YEAR(created_at) = 2024;

-- BAIK: Range query yang bisa pakai index
SELECT * FROM orders
WHERE created_at >= "2024-01-01"
  AND created_at < "2025-01-01";

-- BURUK: LIKE dengan wildcard di awal → full scan
SELECT * FROM products WHERE name LIKE "%laptop%";

-- BAIK: Gunakan Full Text Search
SELECT * FROM products
WHERE MATCH(name) AGAINST("laptop");

Query Cost Estimation

Cost = (jumlah row yang dibaca) × (biaya per operasi)