Oke, saya tidak menyukai ini jadi saya memutuskan untuk mengujinya:
CREATE TABLE userrole (
userid INT,
roleid INT,
PRIMARY KEY (userid, roleid)
);
CREATE INDEX ON userrole (roleid);
Jalankan ini:
<?php
ini_set('max_execution_time', 120); // takes over a minute to insert 500k+ records
$start = microtime(true);
echo "<pre>\n";
mysql_connect('localhost', 'scratch', 'scratch');
if (mysql_error()) {
echo "Connect error: " . mysql_error() . "\n";
}
mysql_select_db('scratch');
if (mysql_error()) {
echo "Selct DB error: " . mysql_error() . "\n";
}
$users = 200000;
$count = 0;
for ($i=1; $i<=$users; $i++) {
$roles = rand(1, 4);
$available = range(1, 5);
for ($j=0; $j<$roles; $j++) {
$extract = array_splice($available, rand(0, sizeof($available)-1), 1);
$id = $extract[0];
query("INSERT INTO userrole (userid, roleid) VALUES ($i, $id)");
$count++;
}
}
$stop = microtime(true);
$duration = $stop - $start;
$insert = $duration / $count;
echo "$count users added.\n";
echo "Program ran for $duration seconds.\n";
echo "Insert time $insert seconds.\n";
echo "</pre>\n";
function query($str) {
mysql_query($str);
if (mysql_error()) {
echo "$str: " . mysql_error() . "\n";
}
}
?>
\n";function query($str) { mysql_query($str); if (mysql_error()) { echo "$str:" . mysql_error() . "\n"; }}?> Keluaran:
499872 users added.
Program ran for 56.5513510704 seconds.
Insert time 0.000113131663847 seconds.
Itu menambahkan 500.000 kombinasi peran pengguna acak dan ada sekitar 25.000 yang cocok dengan kriteria yang dipilih.
Permintaan pertama:
SELECT userid
FROM userrole
WHERE roleid IN (1, 2, 3)
GROUP by userid
HAVING COUNT(1) = 3
Waktu kueri:0,312 detik
SELECT t1.userid
FROM userrole t1
JOIN userrole t2 ON t1.userid = t2.userid AND t2.roleid = 2
JOIN userrole t3 ON t2.userid = t3.userid AND t3.roleid = 3
AND t1.roleid = 1
Waktu kueri:0,016 detik
Betul sekali. Versi gabungan yang saya usulkan dua puluh kali lebih cepat daripada versi agregat.
Maaf tapi saya melakukan ini untuk mencari nafkah dan bekerja di dunia nyata dan di dunia nyata kami menguji SQL dan hasilnya berbicara sendiri.
Alasan untuk ini harus cukup jelas. Kueri agregat akan diskalakan dalam biaya dengan ukuran tabel. Setiap baris diproses, dikumpulkan dan difilter (atau tidak) melalui HAVING
ayat. Versi gabungan akan (menggunakan indeks) memilih subset pengguna berdasarkan peran yang diberikan, lalu memeriksa subset tersebut terhadap peran kedua dan terakhir subset tersebut terhadap peran ketiga. Setiap pilihan
(dalam aljabar relasional
istilah) bekerja pada subset yang semakin kecil. Dari sini Anda dapat menyimpulkan:
Kinerja versi gabungan menjadi lebih baik dengan insiden kecocokan yang lebih rendah.
Jika hanya ada 500 pengguna (dari 500 ribu sampel di atas) yang memiliki tiga peran yang disebutkan, versi bergabung akan menjadi jauh lebih cepat. Versi agregat tidak akan (dan peningkatan kinerja apa pun adalah hasil dari pemindahan 500 pengguna alih-alih 25k, yang juga didapat oleh versi gabungan).
Saya juga penasaran untuk melihat bagaimana database nyata (yaitu Oracle) akan menangani ini. Jadi pada dasarnya saya mengulangi latihan yang sama pada Oracle XE (berjalan pada mesin desktop Windows XP yang sama dengan MySQL dari contoh sebelumnya) dan hasilnya hampir sama.
Penggabungan tampaknya tidak disukai, tetapi seperti yang telah saya tunjukkan, kueri agregat dapat menjadi urutan besarnya lebih lambat.
Pembaruan: Setelah beberapa pengujian ekstensif , gambarnya lebih rumit dan jawabannya akan tergantung pada data Anda, database Anda, dan faktor lainnya. Moral dari cerita ini adalah ujian, ujian, ujian.