Büyük tablolarda RAND() fonksiyonunun veritabanı performansına etkileri

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.png

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.png

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.

Yazar CÆM
Yayınlandı Mar 21, 2019
Paylaşıldı
 
{"currency":[{"name":"US Dollar","abbr":"usd","symbol":"$","type":"fiat","priceBtc":135.51878644593}],"appuser":null}