RAND()

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:

  1. Latar belakang kenapa ORDER BY RAND() sering digunakan
  2. Kenapa ORDER BY RAND() bermasalah
  3. Alternatif dan solusi yang lebih efisien
  4. 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:

  1. Membaca seluruh row dari tabel
  2. Meng-generate nilai random untuk setiap row
  3. Melakukan full sort berdasarkan nilai random
  4. 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 #

  1. Ambil daftar ID saja
  2. Shuffle di aplikasi
  3. 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 query
  • GROUP BY pada tabel besar sangat mahal
  • Tidak scalable

Kenapa ORDER BY COUNT Mahal? #

  1. Database harus:

    • Scan semua row
    • Grouping
    • Sorting hasil aggregate
  2. Tidak bisa pakai index secara optimal

  3. 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 besar
  • ORDER 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
About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact