STRAIGHT_JOIN MySQL ile ne zaman kullanılır?


88

Üzerinde çalıştığım oldukça karmaşık bir sorgu vardı ve çalıştırması 8 saniye sürüyordu. EXPLAIN tuhaf bir tablo düzeni gösteriyordu ve endekslerim FORCE INDEX ipucu ile bile kullanılmıyordu. STRAIGHT_JOIN join anahtar kelimesine rastladım ve bazı INNER JOIN anahtar kelimelerimi bununla değiştirmeye başladım. Önemli bir hız artışı fark ettim. Sonunda bu sorgu için tüm INNER JOIN anahtar kelimelerimi STRAIGHT_JOIN ile değiştirdim ve şimdi 0,01 saniyede çalışıyor.

Sorum şu: STRAIGHT_JOIN'i ne zaman ve INNER JOIN'i ne zaman kullanıyorsunuz? İyi sorgular yazıyorsanız, STRAIGHT_JOIN'i kullanmamanız için herhangi bir neden var mı?

Yanıtlar:


73

STRAIGHT_JOIN'i iyi bir sebep olmadan kullanmanızı tavsiye etmem. Benim kendi deneyimim, MySQL sorgu optimize edicisinin benim istediğimden daha sık zayıf bir sorgu planı seçmesidir, ancak genel olarak onu atlamanız gerekecek kadar sık ​​değildir, bu, her zaman STRAIGHT_JOIN kullanırsanız yaptığınız şeydir.

Benim tavsiyem, tüm sorguları normal JOIN'ler olarak bırakmaktır. Bir sorgunun optimal olmayan bir sorgu planı kullandığını keşfederseniz, önce optimize edicinin daha sonra daha iyi bir sorgu planı seçip seçmeyeceğini görmek için sorguyu biraz yeniden yazmayı veya yeniden yapılandırmayı denemenizi öneririm. Ayrıca, en azından innodb için, yalnızca dizin istatistiklerinizin güncel olmadığından emin olun ( ANALİZ TABLOSU ). Bu, optimize edicinin kötü bir sorgu planı seçmesine neden olabilir. Optimize edici ipuçları genellikle son çareniz olmalıdır.

Sorgu ipuçlarını kullanmamanın bir başka nedeni de, veri dağıtımınızın zamanla değişmesi veya dizin seçiciliğinizin tablonuz büyüdükçe değişebilmesidir. Şu anda en uygun olan sorgu ipuçlarınız zamanla yetersiz hale gelebilir. Ancak iyileştirici, artık güncel olmayan ipuçlarınız nedeniyle sorgu planını uyarlayamayacaktır. Optimize edicinin kararları vermesine izin verirseniz daha esnek kalırsınız.


60
Bu cevap aslında ne zaman kullanılacağını açıklamıyor straight_join.
Pacerier

23

Gönderen MySQL başvuru KATILIN :

"STRAIGHT_JOIN, soldaki tablonun her zaman doğru tablodan önce okunması dışında JOIN'e benzer. Bu, birleştirme iyileştiricisinin tabloları yanlış sıraya koyduğu (birkaç) durum için kullanılabilir."


28
Teşekkürler, ancak bunun üzerindeki MySQL kılavuzunu zaten okudum. Daha fazla açıklama umuyoruz.
Greg

20

İşte kısa süre önce işte ortaya çıkan bir senaryo.

A, B, C olmak üzere üç tabloyu düşünün.

A'nın 3.000 satırı vardır; B'nin 300.000.000 satırı vardır; ve C'nin 2.000 satırı vardır.

Yabancı anahtarlar tanımlanmıştır: B (a_id), B (c_id).

Şuna benzeyen bir sorgunuz olduğunu varsayalım:

select a.id, c.id
from a
join b on b.a_id = a.id
join c on c.id = b.c_id

Deneyimlerime göre, MySQL bu durumda C -> B -> A'yı seçebilir. C, A'dan daha küçüktür ve B çok büyüktür ve hepsi eşittir.

Sorun MySQL'in (C.id ve B.c_id) ile (A.id ve B.a_id) arasındaki kesişim boyutunu hesaba katmamasıdır. B ve C arasındaki birleşim, B kadar çok satır döndürürse, bu çok zayıf bir seçimdir; A ile başlamak, B'yi A kadar çok satıra kadar filtrelemiş olsaydı, çok daha iyi bir seçim olurdu. straight_joinbu emri şu şekilde zorlamak için kullanılabilir:

select a.id, c.id
from a
straight_join b on b.a_id = a.id
join c on c.id = b.c_id

Şimdi adaha önce birleştirilmelib .

Genellikle birleştirmelerinizi, sonuç kümesindeki satır sayısını en aza indiren bir sırayla yapmak istersiniz. Bu nedenle, küçük bir masa ile başlamak ve ortaya çıkan birleşim de küçük olacak şekilde birleştirmek idealdir. Küçük bir masa ile başlayıp onu daha büyük bir masa ile birleştirmek, büyük masa kadar büyük olur.

Yine de istatistiklere bağlıdır. Veri dağılımı değişirse, hesaplama değişebilir. Ayrıca birleştirme mekanizmasının uygulama ayrıntılarına da bağlıdır.

MySQL için gördüğüm en kötü durumlar, gerekli straight_joinveya agresif dizin ipuçlarının tümü, ışık filtrelemeli katı bir sıralama düzeninde çok fazla veriyi sayfalandıran sorgulardır. MySQL, tüm filtreler için dizin kullanmayı şiddetle tercih eder ve sıralı birleştirmeler; Bu mantıklıdır çünkü çoğu insan tüm veritabanını sıralamaya çalışmaz, bunun yerine sorguya yanıt veren sınırlı bir satır alt kümesine sahiptir ve sınırlı bir alt kümeyi sıralamak, sıralı veya sıralı olup olmadığına bakılmaksızın tüm tabloyu filtrelemekten çok daha hızlıdır. değil. Bu durumda, dizine alınmış sütunun bulunduğu tablonun hemen sonrasına düz birleştirme koyarak sabit şeyleri sıralamak istedim.


Sorunu çözmek için düz birleştirmeyi nasıl kullanırsınız?
Hannele

@Hannele straight_joinsağdan önce sol tabloyu değerlendirir. Yani A -> B -> Cbenim örneğimden gitmek isterseniz , ilk joinanahtar kelime ile değiştirilebilir straight_join.
Barry Kelly

Ah temiz. Cevabınıza örnek olarak bunu eklemenizde
fayda var

18

MySQL, karmaşık sorgularda birleştirme sırasını seçmede her zaman iyi değildir. Karmaşık bir sorguyu straight_join olarak belirterek, sorgu birleştirmeleri belirtilen sırayla yürütür. Tabloyu en az ortak payda olacak şekilde yerleştirerek ve straight_join belirterek sorgu performansını iyileştirebilirsiniz.


11

STRAIGHT_JOIN, bu maddeyi kullanarak JOINsıralamayı kontrol edebilirsiniz : hangi tablonun dış döngüde ve hangisinin iç döngüde taranacağı.


Dış döngü ve iç döngü nedir?
Istiaque Ahmed

@IstiaqueAhmed tablolar iç içe döngülerle birleştirilir (tablo A'dan ilk satırı alın ve tablo B'yi döngü atın sonra ikinci satırı alın ... vb. Burada A tablosu dış döngüdedir)
Muhasebeci م

6

STRAIGHT_JOIN'i neden kullanmak zorunda olduğumu size söyleyeceğim:

  • Bir sorguda performans sorunu yaşadım .
  • Sorguyu basitleştirmek, sorgu aniden daha verimli hale geldi
  • Sorunu hangi özel parçanın getirdiğini anlamaya çalışırken, yapamadım. (2 sol birleşme birlikte yavaştı ve her biri bağımsız olarak hızlıydı)
  • Daha sonra EXPLAIN'i hem yavaş hem de hızlı sorgu ile çalıştırdım (sol birleşimlerden birini ekleyin)
  • Şaşırtıcı bir şekilde, MySQL, 2 sorgu arasındaki JOIN siparişlerini tamamen değiştirdi.

Bu nedenle, önceki birleştirmeyi ilk okunması için FORCE'a birleştirmelerden birini straight_join olmaya zorladım. Bu, MySQL'in yürütme sırasını değiştirmesini engelledi ve bir cazibe gibi çalıştı!


2

Kısa deneyimlerime göre, STRAIGHT_JOINsorgumu 30 saniyeden 100 milisaniyeye düşüren durumlardan biri , yürütme planındaki ilk tablonun sütunlara göre sıraya sahip tablo olmamasıdır.

-- table sales (45000000) rows
-- table stores (3) rows
SELECT whatever
FROM 
    sales 
    INNER JOIN stores ON sales.storeId = stores.id
ORDER BY sales.date, sales.id 
LIMIT 50;
-- there is an index on (date, id)

İyileştirici vurmak için seçtiği IF stores ilk o neden olacaktır Using index; Using temporary; Using filesortçünkü

ORDER BY veya GROUP BY, birleştirme kuyruğundaki ilk tablo dışındaki tablolardan sütunlar içeriyorsa, geçici bir tablo oluşturulur.

kaynak

burada optimize edicinin ona salesilk kullanarak vurmasını söyleyerek biraz yardıma ihtiyacı var

sales STRAIGHT_JOIN stores

1
(Cevabınızı süsledim.)
Rick James

2

İle sorgu uçları ise ORDER BY... LIMIT..., bu olabilir optimal yapmaya optimize edici kandırmak için sorguyu yeniden formüle etmek LIMIT önceJOIN .

(Bu Cevap sadece hakkındaki orijinal soru STRAIGHT_JOINiçin geçerli değildir ve tüm durumlar için geçerli değildir.STRAIGHT_JOIN .)

@ Accountant م örneğinden başlayarak , bu çoğu durumda daha hızlı çalışmalıdır. (Ve ipuçlarına ihtiyaç duymaz.)

SELECT  whatever
    FROM  ( SELECT id FROM sales
                ORDER BY  date, id
                LIMIT  50
          ) AS x
    JOIN  sales   ON sales.id = x.id
    JOIN  stores  ON sales.storeId = stores.id
    ORDER BY  sales.date, sales.id;

Notlar:

  • İlk olarak 50 kimlik alınır. Bu özellikle hızlı olacakINDEX(date, id) .
  • Ardından, salesyalnızca 50 "ne olduğunu" almanıza izin vermek için geri birleştirme , geçici bir masada onları çekmeden .
  • bir alt sorgu, tanımı gereği sırasız olduğundan, ORDER BY dış sorguda tekrarlanmalıdır. (Doktor, gerçekten başka bir sıralama yapmaktan kaçınmanın bir yolunu bulabilir.)
  • Evet, daha karışık. Ancak genellikle daha hızlıdır.

İsabet kullanmaya karşıyım çünkü "Bugün daha hızlı olsa bile, yarın daha hızlı olmayabilir."


0

Biraz eski olduğunu biliyorum ama işte bir senaryo, belirli bir tabloyu doldurmak için toplu komut dosyası yapıyorum. Bir noktada, sorgu çok yavaş çalıştı. Birleştirme sırasının belirli kayıtlarda yanlış olduğu görülüyor:

  • Doğru sırada

görüntü açıklamasını buraya girin

  • Kimliği 1 artırmak, siparişi bozar. 'Ekstra' alanına dikkat edin

görüntü açıklamasını buraya girin

  • Straight_join kullanmak sorunu düzeltir

görüntü açıklamasını buraya girin

Milisaniye içinde straight_join çalıştırılırken yaklaşık 65 saniye boyunca yanlış sipariş çalışır


-5
--use 120s, 18 million data
    explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d, tvassist_taid_all t
    WHERE d.taid = t.taid
      AND t.client_version >= '21004007'
      AND t.utdid IS NOT NULL
      AND d.recommend_day = '20170403'
    LIMIT 0, 10000

--use 3.6s repalce by straight join
 explain SELECT DISTINCT d.taid
    FROM tvassist_recommend_list_everyday_diverse d
    STRAIGHT_JOIN 
      tvassist_taid_all t on d.taid = t.taid 
    WHERE 
     t.client_version >= '21004007'
       AND d.recommend_day = '20170403'

      AND t.utdid IS NOT NULL  
    LIMIT 0, 10000

3
Bu size düz birleşimlerin ne zaman uygun olduğunu anlamanız için neredeyse yeterli bilgi vermez.
Hannele
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.