Saya telah bereksperimen dengan Redis dan MongoDB akhir-akhir ini dan tampaknya sering kali ada kasus di mana Anda akan menyimpan berbagai id di MongoDB atau Redis. Saya akan tetap menggunakan Redis untuk pertanyaan ini karena saya bertanya tentang operator MySQL IN .
Saya bertanya-tanya bagaimana kinerjanya untuk mencantumkan sejumlah besar (300-3000) id di dalam operator IN, yang akan terlihat seperti ini:
SELECT id, name, price
FROM products
WHERE id IN (1, 2, 3, 4, ...... 3000)
Bayangkan sesuatu yang sederhana seperti tabel produk dan kategori yang biasanya Anda GABUNG bersama untuk mendapatkan produk dari kategori tertentu . Dalam contoh di atas Anda dapat melihat bahwa di bawah kategori tertentu di Redis ( category:4:product_ids
) saya mengembalikan semua id produk dari kategori dengan id 4, dan menempatkannya di SELECT
kueri di atas di dalam IN
operator.
Bagaimana performanya?
Apakah ini situasi yang "tergantung"? Atau apakah ada yang konkret "ini (tidak) dapat diterima" atau "cepat" atau "lambat" atau haruskah saya menambahkan LIMIT 25
, atau tidakkah itu membantu?
SELECT id, name, price
FROM products
WHERE id IN (1, 2, 3, 4, ...... 3000)
LIMIT 25
Atau haruskah saya memangkas larik id produk yang dikembalikan oleh Redis untuk membatasinya menjadi 25 dan hanya menambahkan 25 id ke kueri daripada 3000 dan LIMIT
-ing ke 25 dari dalam kueri?
SELECT id, name, price
FROM products
WHERE id IN (1, 2, 3, 4, ...... 25)
Setiap saran / umpan balik sangat dihargai!
sumber
id IN (1,2,3 ... 3000)
performanya dibandingkan dengan tabel JOINproducts_categories
. Atau itu yang kamu katakan?IN
klausa (ini bahkan mungkin linier pada daftar yang diurutkan seperti yang Anda tunjukkan, bergantung pada algoritme), dan kemudian perpotongan / pencarian linier .Jawaban:
Secara umum, jika
IN
daftar menjadi terlalu besar (untuk beberapa nilai 'terlalu besar' yang didefinisikan dengan buruk yang biasanya berada di wilayah 100 atau lebih kecil), itu menjadi lebih efisien untuk menggunakan gabungan, membuat tabel sementara jika perlu. untuk menahan angka.Jika angkanya adalah kumpulan yang padat (tidak ada celah - seperti yang disarankan data sampel), Anda dapat melakukannya lebih baik lagi
WHERE id BETWEEN 300 AND 3000
.Namun, mungkin ada celah dalam himpunan, pada titik mana mungkin lebih baik untuk menggunakan daftar nilai yang valid setelah semua (kecuali celahnya relatif sedikit jumlahnya, dalam hal ini Anda dapat menggunakan:
Atau apapun celahnya.
sumber
AND id NOT BETWEEN XXX AND XXX
tidak akan berfungsi dan lebih baik untuk tetap dengan yang setara(x = 1 OR x = 2 OR x = 3 ... OR x = 99)
seperti yang ditulis @David Fells.Saya telah melakukan beberapa tes, dan seperti yang dikatakan David Fells dalam jawabannya , ini dioptimalkan dengan cukup baik. Sebagai referensi, saya telah membuat tabel InnoDB dengan 1.000.000 register dan melakukan seleksi dengan operator "IN" dengan 500.000 nomor acak, hanya dibutuhkan 2,5 detik pada MAC saya; memilih hanya register genap membutuhkan waktu 0,5 detik.
Satu-satunya masalah yang saya miliki adalah saya harus meningkatkan
max_allowed_packet
parameter darimy.cnf
file tersebut. Jika tidak, kesalahan misterius "MYSQL telah hilang" dihasilkan.Berikut kode PHP yang saya gunakan untuk melakukan tes:
$NROWS =1000000; $SELECTED = 50; $NROWSINSERT =15000; $dsn="mysql:host=localhost;port=8889;dbname=testschema"; $pdo = new PDO($dsn, "root", "root"); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); $pdo->exec("drop table if exists `uniclau`.`testtable`"); $pdo->exec("CREATE TABLE `testtable` ( `id` INT NOT NULL , `text` VARCHAR(45) NULL , PRIMARY KEY (`id`) )"); $before = microtime(true); $Values=''; $SelValues='('; $c=0; for ($i=0; $i<$NROWS; $i++) { $r = rand(0,99); if ($c>0) $Values .= ","; $Values .= "( $i , 'This is value $i and r= $r')"; if ($r<$SELECTED) { if ($SelValues!="(") $SelValues .= ","; $SelValues .= $i; } $c++; if (($c==100)||(($i==$NROWS-1)&&($c>0))) { $pdo->exec("INSERT INTO `testtable` VALUES $Values"); $Values = ""; $c=0; } } $SelValues .=')'; echo "<br>"; $after = microtime(true); echo "Insert execution time =" . ($after-$before) . "s<br>"; $before = microtime(true); $sql = "SELECT count(*) FROM `testtable` WHERE id IN $SelValues"; $result = $pdo->prepare($sql); $after = microtime(true); echo "Prepare execution time =" . ($after-$before) . "s<br>"; $before = microtime(true); $result->execute(); $c = $result->fetchColumn(); $after = microtime(true); echo "Random selection = $c Time execution time =" . ($after-$before) . "s<br>"; $before = microtime(true); $sql = "SELECT count(*) FROM `testtable` WHERE id %2 = 1"; $result = $pdo->prepare($sql); $result->execute(); $c = $result->fetchColumn(); $after = microtime(true); echo "Pairs = $c Exdcution time=" . ($after-$before) . "s<br>";
Dan hasilnya:
Insert execution time =35.2927210331s Prepare execution time =0.0161771774292s Random selection = 499102 Time execution time =2.40285992622s Pairs = 500000 Exdcution time=0.465420007706s
sumber
%
) dengan operator sama dengan (=
), bukanIN()
.Anda dapat membuat tabel sementara tempat Anda dapat memasukkan sejumlah ID dan menjalankan kueri bertingkat. Contoh:
CREATE [TEMPORARY] TABLE tmp_IDs (`ID` INT NOT NULL,PRIMARY KEY (`ID`));
dan pilih:
SELECT id, name, price FROM products WHERE id IN (SELECT ID FROM tmp_IDs);
sumber
Penggunaan
IN
dengan set parameter besar pada daftar besar record sebenarnya akan lambat.Dalam kasus yang saya selesaikan baru-baru ini, saya memiliki dua klausa di mana, satu dengan 2.50 parameter dan yang lainnya dengan 3.500 parameter, menanyakan tabel 40 Juta catatan.
Permintaan saya memakan waktu 5 menit menggunakan standar
WHERE IN
. Dengan menggunakan subkueri untuk pernyataan IN (meletakkan parameter di tabel terindeksnya sendiri), saya menurunkan kueri menjadi DUA detik.Bekerja untuk MySQL dan Oracle menurut pengalaman saya.
sumber
IN
baik-baik saja, dan dioptimalkan dengan baik. Pastikan Anda menggunakannya di bidang yang diindeks dan Anda baik-baik saja.Secara fungsional setara dengan:
Sejauh menyangkut mesin DB.
sumber
IN
menggunakan pengoptimalan untuk kinerja yang lebih baik.Saat Anda memberikan banyak nilai untuk
IN
operator, pertama kali harus mengurutkannya untuk menghapus duplikat. Setidaknya saya curiga. Jadi tidak baik memberikan terlalu banyak nilai, karena pengurutan membutuhkan waktu N log N.Pengalaman saya membuktikan bahwa mengiris kumpulan nilai menjadi subset yang lebih kecil dan menggabungkan hasil dari semua kueri dalam aplikasi akan memberikan kinerja terbaik. Saya akui bahwa saya mengumpulkan pengalaman pada database yang berbeda (Pervasive), tetapi hal yang sama mungkin berlaku untuk semua mesin. Hitungan nilai saya per set adalah 500-1000. Lebih atau kurang secara signifikan lebih lambat.
sumber