1. 2

Veritabanından rastgele sonuçlar getirmek için genelde şöyle bir sorgu kullanırız

SELECT * FROM deneme ORDER BY RAND() LIMIT 5

Bu sorgu deneme tablosundan 5 adet rastgele sonuç getirir. Fakat bu rastgele sonuçlar getirmek için çok kötü bir yol.

Tablonuzda 100-200 kayıt varsa problem yok da ya yüzbinlerce kayıt varsa?

Diyelim ki 50 bin satırlık bir tablomuz var ve RAND() fonksiyonu yardımıyla böyle bir sorgu çalıştırdık, arka planda neler olur? 50 bin satırın her biri için random bir sayı oluşturulur, tüm tablo buna göre sıralanır ve istediğiniz kesit bundan sonra getirilir! Tablonuz ne kadar büyükse veritabanına yükleyeceğiniz yük o kadar artacaktır. Tablo büyüdükçe sorgu hızı bakın nasıl düşüyor.

drmpbsca4pvjqcwcdsig

Dolayısıyla rastgele sonuçlar getirmek için RAND() kullanmak büyük tablolar için tam bir performans katliamı olacaktır.

Peki ne yapacağız? Rastgeleliği veritabanı seviyesinde değil yazılım seviyesinde elde edeceğiz. Bunun için kullandığınız yazılım dilinde rastgele sayı üretebilirsiniz. Fakat rastgele üretilen sayının veritabanındaki kayıtlardan çok olmaması gerekiyor, bu yüzden önce tabloda kaç kayıt olduğunu küçük bir sorguyla getirin.

$toplam_sayi = SELECT COUNT(id) FROM deneme;
$rastgele_sayi = rand(1, $toplam_sayi);
SELECT * FROM deneme LIMIT 5 OFFSET $rastgele_sayi;

Bu şekilde OFFSET'i rastgele bir kayıttan başlatarak rastgele 5 kayıt getiriyoruz. Bir yerine iki sorgu olması kafa karıştırmasın çünkü toplam sayıyı sorgulamak için çalıştırılan sorgu performans açısından sıkıntı yaratmayacak kadar küçük bir işlem ve diğer seçenekden çok daha hızlı.

Bu yöntemin handikapı rastgele bir başlangıç noktası belirleyip sırasıyla 5 sonuç getirmesidir. Diyelim ki $rastgele_sayi 100 olduğu sürece hep 100'den 105'e kadar olan kayıtlar gelecektir. Eğer bu istenmeyen bir durumsa farklı yollar denenebilir. Böyle bir durumda getirilecek kayıt sayısı kadar rastgele id üretilerek bir sorgu oluşturulabilir.

// Tablodaki kayıtlardan en büyük id'yi getirelim
$max_id = SELECT MAX(id) FROM deneme;

/* Yazılım seviyesinde 1 ile $max_id arasında rastgele sayılar üretelim. 
 * Tablonuzda bazı kayıtların silinmiş olma ihtimali yüksektir. 
 * Bu yüzden 5 kayıt getirmek istesek de 3-5 katı kadar rastgele id seçelim.
 */
for($i = 1; $i < 30; $i++){
    $rastgele_idler[] = rand(1, $max_id);
}

// Bu id'leri IN() sorgusu içinde çalışabilecek şekilde virgülle ayrılmış satır haline getirelim
$rastgele_idler = join($rastgele_idler, ',');

// Sorguyu yazalım ve istediğimiz limiti belirleyelim
SELECT * FROM deneme WHERE id IN ($rastgele_idler) LIMIT 5;

Bu iki yöntemin RAND() kullanmaktan nasıl daha hızlı olduğunu ilk tablo ile aşağıdakini karşılaştırarak görebiliriz.

mrlqoviof03f3dhzm1b0

Bu arada sorguları gerçek bir benchmarka tabi tutmak lazım, ben gelişigüzel konsoldan denedim kabaca gösterebilmek için.

Random sonuçlar getirmek veritabanları için gördüğünüz gibi biraz problemli. Hatta bildiğim bazı ORM'ler RAND() fonksiyonunu bu sebeple desteklemiyor.

Hem kendi çözüm yöntemimi aktarmak, hem de başka fikirleri tecrübesi olanlardan almak istedim.

1 yanıt
  1. 1
    Emre Özdemir
    Ünvan yok · 1 ay önce

    Benim bazı itirazlarım, daha doğrusu itirazımsı sorularım var.

    1. Bu performans kaybı özel olarak RAND() fonksiyonundan değil tablodaki her kayıt için bir hesaplama yapılması veya fonksiyon çalıştırılmasından kaynaklanıyor.

    Tablo yapısında doğal olarak bulunmayan (yani halihazırda sütun olarak bulunmayan) her hesaplanmış sütun sorguya ekstra bir yük getiriyor.

    Dolayısıyla büyük tablolarda sadece RAND() ile değil başka bir şekilde de sorguya hesaplama kattığımız anda büyük performans kayıpları yaşayabiliriz.

    2. RAND() için alternatif yollar üretebiliyoruz fakat örneğin gönderiler beğeni sayısına göre sıralamak istediğimizde nasıl bir alternatif yol izleyebiliriz? Bu çoğu zaman mümkün değil. Ve gelişmiş uygulamalarda hesaplanmış sütun kullanmak hemen hemen şart. Bunlardan nasıl vazgeçeceğiz?

    3. Diyelim ki RAND()'ı sorguya dahil ederek performans kaybetmeyi göze aldık ya da buna mecbur kaldık; peki aynı sorguya ikinci bir hesaplanmış sütun eklemek performansı bir o kadar daha mı etkileyecektir, yoksa hemen hemen aynı mı olacaktır?