Saya memiliki permintaan yang membutuhkan waktu sangat lama untuk dijalankan (15+ detik) dan semakin lama semakin buruk seiring bertambahnya dataset saya. Saya telah mengoptimalkan ini di masa lalu, dan telah menambahkan indeks, penyortiran tingkat kode dan optimisasi lainnya, tetapi perlu beberapa penyempurnaan lebih lanjut.
SELECT sounds.*, avg(ratings.rating) AS avg_rating, count(ratings.rating) AS votes FROM `sounds`
INNER JOIN ratings ON sounds.id = ratings.rateable_id
WHERE (ratings.rateable_type = 'Sound'
AND sounds.blacklisted = false
AND sounds.ready_for_deployment = true
AND sounds.deployed = true
AND sounds.type = "Sound"
AND sounds.created_at > "2011-03-26 21:25:49")
GROUP BY ratings.rateable_id
Tujuan kueri adalah untuk memberi saya sound id
peringkat rata-rata dari suara terbaru yang dirilis. Ada sekitar 1500 suara, dan peringkat 2 Juta.
Saya memiliki beberapa indeks sounds
mysql> show index from sounds;
+--------+------------+------------------------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+————+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+--------+------------+------------------------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+————+
| sounds | 0 | PRIMARY | 1 | id | A | 1388 | NULL | NULL | | BTREE | |
| sounds | 1 | sounds_ready_for_deployment_and_deployed | 1 | deployed | A | 5 | NULL | NULL | YES | BTREE | |
| sounds | 1 | sounds_ready_for_deployment_and_deployed | 2 | ready_for_deployment | A | 12 | NULL | NULL | YES | BTREE | |
| sounds | 1 | sounds_name | 1 | name | A | 1388 | NULL | NULL | | BTREE | |
| sounds | 1 | sounds_description | 1 | description | A | 1388 | 128 | NULL | YES | BTREE | |
+--------+------------+------------------------------------------+--------------+----------------------+-----------+-------------+----------+--------+------+------------+---------+
dan beberapa ratings
mysql> show index from ratings;
+---------+------------+-----------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+————+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment |
+---------+------------+-----------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+————+
| ratings | 0 | PRIMARY | 1 | id | A | 2008251 | NULL | NULL | | BTREE | |
| ratings | 1 | index_ratings_on_rateable_id_and_rating | 1 | rateable_id | A | 18 | NULL | NULL | | BTREE | |
| ratings | 1 | index_ratings_on_rateable_id_and_rating | 2 | rating | A | 9297 | NULL | NULL | YES | BTREE | |
+---------+------------+-----------------------------------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+
Ini dia EXPLAIN
mysql> EXPLAIN SELECT sounds.*, avg(ratings.rating) AS avg_rating, count(ratings.rating) AS votes FROM sounds INNER JOIN ratings ON sounds.id = ratings.rateable_id WHERE (ratings.rateable_type = 'Sound' AND sounds.blacklisted = false AND sounds.ready_for_deployment = true AND sounds.deployed = true AND sounds.type = "Sound" AND sounds.created_at > "2011-03-26 21:25:49") GROUP BY ratings.rateable_id;
+----+-------------+---------+--------+--------------------------------------------------+-----------------------------------------+---------+-----------------------------------------+---------+——————+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+--------+--------------------------------------------------+-----------------------------------------+---------+-----------------------------------------+---------+——————+
| 1 | SIMPLE | ratings | index | index_ratings_on_rateable_id_and_rating | index_ratings_on_rateable_id_and_rating | 9 | NULL | 2008306 | Using where |
| 1 | SIMPLE | sounds | eq_ref | PRIMARY,sounds_ready_for_deployment_and_deployed | PRIMARY | 4 | redacted_production.ratings.rateable_id | 1 | Using where |
+----+-------------+---------+--------+--------------------------------------------------+-----------------------------------------+---------+-----------------------------------------+---------+-------------+
Saya melakukan cache hasil yang pernah didapat, sehingga kinerja situs tidak banyak masalah, tetapi penghangat cache saya membutuhkan waktu lebih lama dan lebih lama untuk dijalankan karena panggilan ini memakan waktu begitu lama, dan itu mulai menjadi masalah. Ini sepertinya tidak banyak angka untuk dikacaukan dalam satu permintaan ...
Apa lagi yang bisa saya lakukan untuk membuat ini tampil lebih baik ?
sumber
EXPLAIN
hasilnya?EXPLAIN SELECT sounds.*, avg(ratings.rating) AS avg_rating, count(ratings.rating) AS votes FROM sounds INNER JOIN ratings ON sounds.id = ratings.rateable_id WHERE (ratings.rateable_type = 'Sound' AND sounds.blacklisted = false AND sounds.ready_for_deployment = true AND sounds.deployed = true AND sounds.type = "Sound" AND sounds.created_at > "2011-03-26 21:25:49") GROUP BY ratings.rateable_id
Jawaban:
Setelah memeriksa kueri, tabel, dan klausa WHERE AND GROUP BY, saya merekomendasikan yang berikut ini:
Rekomendasi # 1) Refactor the Query
Saya mengatur ulang kueri untuk melakukan tiga (3) hal:
Inilah permintaan yang saya ajukan:
Rekomendasi # 2) Buat indeks tabel suara dengan indeks yang akan mengakomodasi klausa WHERE
Kolom indeks ini mencakup semua kolom dari klausa WHERE dengan nilai statis pertama dan target bergerak terakhir
Saya sungguh percaya Anda akan terkejut. Cobalah !!!
UPDATE 2011-05-21 19:04
Saya baru saja melihat kardinalitasnya. Aduh !!! Kardinalitas 1 untuk rateable_id. Wah, aku merasa bodoh !!!
UPDATE 2011-05-21 19:20
Mungkin membuat indeks akan cukup untuk memperbaiki keadaan.
UPDATE 2011-05-21 22:56
Silakan jalankan ini:
UPDATE 2011-05-21 23:34
Saya refactored lagi. Tolong Coba Yang Ini:
UPDATE 2011-05-21 23:55
Saya refactored lagi. Silakan Coba Yang Terakhir Ini (Terakhir Kali):
UPDATE 2011-05-22 00:12
Aku benci menyerah !!!!
UPDATE 2011-05-22 07:51
Sudah mengganggu saya bahwa peringkat akan kembali dengan 2 juta baris di EXPLAIN. Lalu, aku tersadar. Anda mungkin perlu indeks lain di tabel peringkat yang dimulai dengan rateable_type:
Tujuan dari indeks ini adalah untuk mengurangi tabel temp yang memanipulasi peringkat sehingga kurang dari 2 juta. Jika kami bisa mendapatkan tabel temp secara signifikan lebih kecil (setidaknya setengah), maka kami dapat memiliki harapan yang lebih baik dalam kueri Anda dan tambang saya bekerja lebih cepat juga.
Setelah membuat indeks itu, silakan Coba lagi permintaan yang diajukan asli saya dan juga coba milik Anda:
UPDATE 2011-05-22 18:39: FINAL WORDS
Saya telah refactored permintaan dalam prosedur tersimpan dan menambahkan indeks untuk membantu menjawab pertanyaan tentang mempercepat hal-hal. Saya mendapat 6 suara positif, mendapat jawaban yang diterima, dan menerima 200 hadiah.
Saya juga refactored permintaan lain (hasil marginal) dan menambahkan indeks (hasil dramatis). Saya mendapat 2 suara positif dan jawaban diterima.
Saya menambahkan indeks untuk tantangan permintaan lain dan telah di-upgrade sekali
dan sekarang pertanyaanmu .
Ingin menjawab semua pertanyaan seperti ini (termasuk pertanyaan Anda) terinspirasi oleh video YouTube yang saya tonton di pertanyaan refactoring.
Sekali lagi terima kasih, @coneybeare !!! Saya ingin menjawab pertanyaan ini semaksimal mungkin, tidak hanya menerima poin atau pujian. Sekarang, saya bisa merasakan bahwa saya mendapatkan poin !!!
sumber
Terima kasih atas output EXPLAIN. Seperti yang Anda tahu dari pernyataan itu, alasan waktu yang lama adalah tablescan penuh pada tabel peringkat. Tidak ada dalam pernyataan WHERE yang menyaring 2 juta baris.
Anda dapat menambahkan indeks pada ratings.type, tetapi tebakan saya adalah CARDINALITY akan menjadi sangat rendah dan Anda masih akan memindai beberapa baris
ratings
.Atau Anda dapat mencoba menggunakan petunjuk indeks untuk memaksa mysql menggunakan indeks suara.
Diperbarui:
Jika itu saya, saya akan menambahkan indeks
sounds.created
sebagai yang memiliki kesempatan terbaik untuk menyaring baris dan mungkin akan memaksa pengoptimal permintaan mysql untuk menggunakan indeks tabel suara. Berhati-hatilah dengan pertanyaan yang menggunakan jangka waktu yang lama (1 tahun, 3 bulan, hanya tergantung pada ukuran tabel suara).sumber
Jika ini harus menjadi permintaan yang tersedia "on-the-fly" , maka itu membatasi opsi Anda sedikit.
Saya akan menyarankan membagi dan menaklukkan untuk masalah ini.
sumber
sounds
,ratings
ke permintaan tengah), tapi itu mengunci kotak sql saya dan saya harus mematikan prosesnya.Gunakan GABUNG, bukan subkueri. Apakah ada upaya subquery Anda yang membantu?
TAMPILKAN MENCIPTAKAN MEJA terdengar \ G
Tunjukkan peringkat CREATE TABLE \ G
Seringkali menguntungkan memiliki indeks "gabungan", bukan indeks satu kolom. Mungkin INDEX (ketik, Created_at)
Anda memfilter pada kedua tabel dalam GABUNG; yang mungkin menjadi masalah kinerja.
Rekomendasikan Anda memiliki id auto_increment
ratings
, buat tabel ringkasan, dan gunakan id AI untuk melacak di mana Anda "tinggalkan". Namun, jangan menyimpan rata-rata dalam tabel ringkasan:Alih-alih, pertahankan SUM (peringkat. Pemberian). Rata-rata rata-rata secara matematis tidak benar untuk menghitung rata-rata; (jumlah penjumlahan) / (jumlah penghitungan) benar.
sumber