RAND() #
Dalam dunia backend dan data-intensive application, performa query database sering kali menjadi bottleneck tersembunyi. Salah satu anti-pattern yang sangat sering ditemui—bahkan di sistem produksi—adalah penggunaan:
ORDER BY RAND()
Sekilas terlihat sederhana dan “langsung jadi”. Namun di balik itu, ada biaya performa yang sangat mahal dan sering tidak disadari.
Artikel ini akan membahas:
- Latar belakang kenapa
ORDER BY RAND()sering digunakan - Kenapa
ORDER BY RAND()bermasalah - Alternatif dan solusi yang lebih efisien
- Best practice untuk kebutuhan random order dan ORDER BY COUNT
Latar Belakang #
Use case paling umum:
- Menampilkan data secara acak (random content)
- Featured item / rekomendasi acak
- Quiz, soal random, banner random
- Sampling data kecil dari tabel besar
Contoh klasik:
SELECT *
FROM articles
ORDER BY RAND()
LIMIT 10;
Secara developer experience, ini:
- Mudah ditulis
- Mudah dipahami
- Tidak perlu logic tambahan
Masalahnya: mudah ≠ murah.
Kenapa Buruk untuk Performa? #
Cara Kerja ORDER BY RAND()
#
Secara internal, database harus:
- Membaca seluruh row dari tabel
- Meng-generate nilai random untuk setiap row
- Melakukan full sort berdasarkan nilai random
- Baru mengambil
LIMIT N
Artinya:
Time complexity ≈ O(n log n) untuk jumlah row
n
Jika tabel berisi:
- 10 ribu row → masih terasa “aman”
- 1 juta row → mulai berat
- 10 juta row → disaster
Index Tidak Bisa Digunakan #
RAND():
- Bukan kolom
- Tidak deterministic
- Tidak bisa di-index
Akibatnya:
- Selalu full table scan
- Selalu full sort
- Tidak peduli seberapa bagus index kamu
Dampak di Production #
Efek nyata di sistem live:
- CPU spike di database
- Query latency meningkat
- Lock contention
- Request timeout
- Efek domino ke service lain
Banyak kasus:
“Aplikasi lambat” padahal akar masalahnya cuma satu query
ORDER BY RAND().
Alternatif & Solusi #
Random Berdasarkan Primary Key (ID Sampling) #
Jika ID bersifat numerik dan relatif kontinu:
SELECT *
FROM articles
WHERE id >= FLOOR(RAND() * (SELECT MAX(id) FROM articles))
ORDER BY id
LIMIT 10;
Kelebihan
- Tidak full scan
- Lebih cepat
- Index-friendly
Kekurangan
- Distribusi tidak benar-benar random
- Tidak cocok jika banyak ID bolong (deleted rows)
Precomputed Random Value #
Tambahkan kolom:
random_value FLOAT
Isi sekali saat insert/update:
INSERT INTO articles (..., random_value)
VALUES (..., RAND());
Query:
SELECT *
FROM articles
ORDER BY random_value
LIMIT 10;
Kelebihan
- Indexable
- Query sangat cepat
- Stabil di production
Kekurangan
- Random tidak berubah kecuali di-refresh
- Perlu mekanisme regenerate jika ingin variasi
Ini adalah best practice untuk random content skala besar.
Random ID List di Application Layer #
- Ambil daftar ID saja
- Shuffle di aplikasi
- Query ulang berdasarkan ID terpilih
SELECT id FROM articles;
Shuffle di app:
rand.Shuffle(len(ids), ...)
Query final:
SELECT * FROM articles WHERE id IN (...);
Kelebihan
- Database ringan
- Kontrol penuh di aplikasi
Kekurangan
- Tidak cocok jika ID sangat banyak
- Memory di aplikasi meningkat
Bucket / Partition Random #
Gunakan modulo:
SELECT *
FROM articles
WHERE id % 10 = FLOOR(RAND() * 10)
LIMIT 10;
Pendekatan ini:
- Mengurangi jumlah row yang di-scan
- Masih cukup random secara statistik
Best Practice #
Sekarang kita masuk ke masalah yang lebih sering muncul di sistem real-world.
Contoh Masalah #
SELECT article_id, COUNT(*) AS total
FROM views
GROUP BY article_id
ORDER BY total DESC
LIMIT 10;
Ini terlihat wajar, tapi:
COUNT(*)dilakukan setiap queryGROUP BYpada tabel besar sangat mahal- Tidak scalable
Kenapa ORDER BY COUNT Mahal?
#
Database harus:
- Scan semua row
- Grouping
- Sorting hasil aggregate
Tidak bisa pakai index secara optimal
Cost meningkat linear dengan data growth
Solusi & Best Practice #
Pre-Aggregated Table (Counter Table) #
Gunakan tabel khusus:
article_stats
------------
article_id
view_count
like_count
comment_count
Update secara incremental:
UPDATE article_stats
SET view_count = view_count + 1
WHERE article_id = ?;
Query jadi:
SELECT *
FROM article_stats
ORDER BY view_count DESC
LIMIT 10;
Ini adalah GOLD STANDARD.
Eventual Consistency (Batch Update) #
- Hitung count via batch job
- Cron / worker
- Update setiap X menit
Cocok untuk:
- Trending content
- Dashboard
- Ranking non-realtime
Cache Layer (Redis) #
Untuk traffic tinggi:
- Increment counter di Redis
- Periodic flush ke database
- Query ranking langsung dari Redis
Benefit:
- DB sangat ringan
- Latency rendah
- Scalable
Anti-Pattern yang Harus Dihindari #
❌ ORDER BY COUNT(*) di query user-facing
❌ Aggregate langsung di tabel transaksi besar
❌ Mengandalkan DB untuk ranking real-time tanpa cache
Ringkasan Best Practice #
Hindari: #
ORDER BY RAND()di tabel besarORDER BY COUNT(*)on-the-fly
Gunakan: #
- Precomputed random value
- Counter table
- Cache (Redis)
- Batch aggregation
Prinsip utamanya: Database itu jago menyimpan & mencari, bukan mengocok dan menghitung ulang tanpa henti.
Penutup #
Query yang “kelihatannya kecil” sering menjadi penyebab utama masalah performa di sistem besar.
ORDER BY RAND() dan ORDER BY COUNT() adalah contoh klasik di mana kemudahan menulis query berbanding terbalik dengan skalabilitas.
Sebagai engineer, tanggung jawab kita bukan hanya membuat fitur jalan, tapi:
- Jalan cepat
- Jalan stabil
- Jalan bertahun-tahun ke depan