MySQL başka bir tabloya katılırken dizin kullanmıyor


11

İki tablo var, ilk tablo bir CMS içindeki tüm makaleler / blog gönderileri içerir. Bu makalelerin bazıları bir dergide de görünebilir, bu durumda dergiye özgü bilgiler içeren başka bir tablo ile yabancı anahtar ilişkisi vardır.

Aşağıda, bu iki tablo için tablo oluşturma sözdiziminin basitleştirilmiş bir sürümü bulunmaktadır ve bazı temel olmayan satırlar çıkarılmıştır:

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 toplamda yaklaşık 250.000 makale içeriyor ve bu sorunu yerel olarak çoğaltmak istiyorlarsa, bir test veritabanını örnek verilerle doldurmak için kullanılabilecek basit bir Python betiği yazdım .

Bu tablolardan birini seçersem, MySQL uygun bir dizin seçmek ya da makaleleri hızlı bir şekilde almakta sorun yaşamaz. Ancak, iki tablo aşağıdaki gibi basit bir sorguda birleştirildiğinde:

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 uygun bir sorgulama ve performans düşüşü seçemez. İlgili genişletilmiş açıklama (bir saniyenin üzerinde olan yürütme süresi):

+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
| 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                     |
+----+-------------+--------------+--------+-----------------------------------+---------+---------+----------------------------------------+-------+----------+---------------------------------+
  • 30 Eylül EDIT: WHEREyan tümceyi bu sorgudan kaldırabilir , ancak EXPLAINyine de aynı görünüyor ve sorgu hala yavaş.

Potansiyel bir çözüm, bir endeksi zorlamaktır. Aynı sorguyu FORCE INDEX (base_articel_date_published)yaklaşık 1,6 milisaniyede çalışan bir sorguda sonuçlarla çalıştırmak.

+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+
| 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    |             |
+----+-------------+--------------+--------+---------------+-----------------------------+---------+-------------------------+------+-----------+-------------+

Birkaç nedenden ötürü, bu sorguyu önlemek için bir dizin zorlamak zorunda değil tercih ederim. En önemlisi, bu temel sorgu çeşitli şekillerde (ile filtreleme gibi) filtrelenebilir / değiştirilebilir; issue_slugbundan sonra base_article_date_publishedartık en iyi endeks kullanılmayabilir.

Herkes bu sorgu için performansı artırmak için bir strateji önerebilir?


"is_published" sütunu yalnızca iki veya üç değer tutarsa, gerçekten bu dizini bırakabilirsiniz KEY base_article_is_published( is_published) .. bana bakıyor boolean bir tip ..
Raymond Nijland

cevabı düzenledi
Raymond Nijland

Yanıtlar:


5

Buna ne dersiniz, veriler zaten doğru sıralamada olduğu için, "Geçici kullanma; filesort kullanma" ihtiyacını ortadan kaldırmalıdır.

Bu ihtiyacı gidermek için neden MySQL'in "geçici kullanma; filesort kullanma" yı gerektirdiğini bilmeniz gerekir.

İhtiyacı ortadan kaldırma hakkında açıklama için ikinci kare göze bakın

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

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

Oldukça iyi çalışıyor Ben de bir süre önce Ülke / şehir tabloları için örnek veri ile burada demo bakın http://sqlfiddle.com/#!2/b34870/41

Base_article.is_published = 1 her zaman açıkladığınız gibi 1 kayıt döndürürse, bu cevabı analiz etmek isteyebilirsiniz.

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


Hayat kurtaran cevap! Sadece kullanıyordum JOINama MySQL dizin almıyordu. Çok teşekkürler Raymond
Maximus

4

SOĞUTUCU SORGULAMA

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;

veya

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;

ENDEKSLERİNİZİ DEĞİŞTİRİN

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

BİR ŞANS VER !!!


Refactor: Çalışmıyor korkarım, çünkü LIMIT 30alt sorgudadır (bu 30 satırın hepsi de mag_articlestabloda olmayacaktır ). LIMITDış sorguyu taşıdığımda performans orijinalimdeki ile aynıdır. Dizinleri Değiştir: MySQL de bu dizini kullanmaz. Cümleyi WHEREorijinal sorgumdan kaldırmak bir fark yaratmıyor gibi görünüyor.
Joshmaker

İkinci refactor yöntemi inanılmaz iyi çalıştı, sorgu süresi masamda 8 saniyeden 0,3 saniyeye önemli ölçüde azaldı ... teşekkür ederim efendim !!
andreszs
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.