MySQL tidak menggunakan indeks saat bergabung dengan tabel lain

11

Saya punya dua tabel, tabel pertama berisi semua artikel / posting blog dalam CMS. Beberapa artikel ini juga dapat muncul di majalah, dalam hal ini mereka memiliki hubungan kunci asing dengan tabel lain yang berisi informasi spesifik majalah.

Berikut ini adalah versi sederhana dari sintaks membuat tabel untuk dua tabel ini dengan beberapa baris yang tidak penting dihapus:

CREATE TABLE `base_article` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `date_published` datetime DEFAULT NULL,
  `title` varchar(255) NOT NULL,
  `description` text,
  `content` longtext,
  `is_published` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  KEY `base_article_date_published` (`date_published`),
  KEY `base_article_is_published` (`is_published`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `mag_article` (
    `basearticle_ptr_id` int(11) NOT NULL,
    `issue_slug` varchar(8) DEFAULT NULL,
    `rubric` varchar(75) DEFAULT NULL,
    PRIMARY KEY (`basearticle_ptr_id`),
    KEY `mag_article_issue_slug` (`issue_slug`),
    CONSTRAINT `basearticle_ptr_id_refs_id` FOREIGN KEY (`basearticle_ptr_id`) REFERENCES `base_article` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CMS berisi sekitar 250.000 artikel total dan saya telah menulis skrip Python sederhana yang dapat digunakan untuk mengisi basis data uji dengan data sampel jika mereka ingin mereplikasi masalah ini secara lokal.

Jika saya memilih dari salah satu tabel ini, MySQL tidak memiliki masalah memilih indeks yang sesuai atau mengambil artikel dengan cepat. Namun, saat kedua tabel digabungkan bersama dalam kueri sederhana seperti:

SELECT * FROM `base_article` 
INNER JOIN `mag_article` ON (`mag_article`.`basearticle_ptr_id` = `base_article`.`id`)
WHERE is_published = 1
ORDER BY `base_article`.`date_published` DESC
LIMIT 30

MySQL gagal memilih kueri yang sesuai dan kinerja menurun. Berikut penjelasan yang relevan diperpanjang (waktu eksekusi lebih dari satu detik):

+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| id | select_type |    table     |  type  |           possible_keys           |   key   | key_len |                  ref                   | rows  | filtered |              Extra              |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
|  1 | SIMPLE      | mag_article  | ALL    | PRIMARY                           | NULL    | NULL    | NULL                                   | 23830 | 100.00   | Using temporary; Using filesort |
|  1 | SIMPLE      | base_article | eq_ref | PRIMARY,base_article_is_published | PRIMARY | 4       | my_test.mag_article.basearticle_ptr_id |     1 | 100.00   | Using where                     |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
  • EDIT SEPTEMBER 30: Saya bisa menghapus WHEREklausa dari kueri ini, tetapi klausa EXPLAINmasih terlihat sama dan kueri masih lambat.

Salah satu solusi potensial adalah dengan memaksa indeks. Menjalankan kueri yang sama dengan FORCE INDEX (base_articel_date_published)hasil dalam kueri yang dijalankan sekitar 1,6 milidetik.

+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| id | select_type |    table     |  type  | possible_keys |             key             | key_len |           ref           | rows | filtered  |    Extra    |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
|  1 | SIMPLE      | base_article | index  | NULL          | base_article_date_published |       9 | NULL                    |   30 | 833396.69 | Using where |
|  1 | SIMPLE      | mag_article  | eq_ref | PRIMARY       | PRIMARY                     |       4 | my_test.base_article.id |    1 | 100.00    |             |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+

Saya lebih suka tidak harus memaksa indeks pada permintaan ini jika saya bisa menghindarinya, karena beberapa alasan. Paling khusus, permintaan dasar ini dapat difilter / dimodifikasi dalam berbagai cara (seperti pemfilteran oleh issue_slug) setelah itu base_article_date_publishedmungkin tidak lagi menjadi indeks terbaik untuk digunakan.

Adakah yang bisa menyarankan strategi untuk meningkatkan kinerja untuk permintaan ini?

Joshmaker
sumber
jika kolom "is_published" hanya menyimpan dua atau tiga nilai, Anda benar-benar dapat menjatuhkan indeks KUNCI base_article_is_published( is_published) .. menurut saya ini adalah tipe boolean ..
Raymond Nijland
diedit jawabannya
Raymond Nijland

Jawaban:

5

Bagaimana dengan ini, ini harus menghapus kebutuhan untuk "Menggunakan sementara; Menggunakan filesort" karena data sudah dalam jenis yang tepat.

Anda perlu mengetahui trik mengapa MySQL perlu "Menggunakan sementara; Menggunakan filesort" untuk menghapus kebutuhan itu.

Lihat sqlfriddle kedua untuk penjelasan tentang menghapus kebutuhan

SELECT
      *
    FROM base_article

    STRAIGHT_JOIN 
      mag_article
    ON
      (mag_article.basearticle_ptr_id = base_article.id)

    WHERE
      base_article.is_published = 1

    ORDER BY
      base_article.date_published DESC

lihat http://sqlfiddle.com/#!2/302710/2

Bekerja cukup baik saya membutuhkan ini juga beberapa waktu lalu untuk tabel Negara / kota lihat demo di sini dengan contoh data http://sqlfiddle.com/#!2/b34870/41

Diedit Anda juga mungkin ingin menganalisis jawaban ini jika base_article.is_published = 1 selalu mengembalikan 1 catatan seperti penjelasan Anda menjelaskan tabel deliverd INNER JOIN dapat memberikan kinerja yang lebih baik seperti pertanyaan dalam jawaban di bawah ini

/programming/18738483/mysql-slow-query-using-filesort/18774937#18774937

Raymond Nijland
sumber
Jawaban yang menyelamatkan jiwa! Saya JOINhanya menggunakan tetapi MySQL tidak mengambil indeks. Terima kasih banyak, Raymond
Maximus
4

REFACTOR QUERY THE

SELECT * FROM
(SELECT * FROM base_article
WHERE is_published = 1
ORDER BY date_published LIMIT 30) A
INNER JOIN mag_article B
ON A.id = B.basearticle_ptr_id;

atau

SELECT B.*,C.* FROM
(SELECT id FROM base_article
WHERE is_published = 1
ORDER BY date_published LIMIT 30) A
LEFT JOIN base_article ON A.id = B.id
LEFT JOIN mag_article C ON B.id = C.basearticle_ptr_id;

MODIFIKASI INDEKS ANDA

ALTER TABLE base_article DROP INDEX base_article_is_published;
ALTER TABLE base_article ADD INDEX ispub_datepub_index (is_published,date_published);

COBALAH !!!

RolandoMySQLDBA
sumber
Refactor: Tidak berfungsi, saya takut, karena LIMIT 30ada di subquery (tidak semua dari 30 baris itu juga ada di mag_articlestabel). Jika saya memindahkan LIMITke kueri luar, kinerjanya sama dengan yang asli. Ubah Indeks: MySQL juga tidak menggunakan indeks itu. Menghapus WHEREklausa dari permintaan awal saya sepertinya tidak membuat perbedaan.
Joshmaker
Metode refactor kedua bekerja sangat baik, waktu kueri telah berkurang secara dramatis dari 8 detik menjadi 0,3 detik di meja saya ... terima kasih Pak !!
andreszs