Kapan menggunakan STRAIGHT_JOIN dengan MySQL

88

Saya baru saja memiliki kueri yang cukup kompleks yang sedang saya kerjakan dan butuh 8 detik untuk menjalankannya. JELASKAN menunjukkan urutan tabel yang aneh dan indeks saya tidak semuanya digunakan bahkan dengan petunjuk FORCE INDEX. Saya menemukan kata kunci gabungan STRAIGHT_JOIN dan mulai mengganti beberapa kata kunci INNER JOIN saya dengannya. Saya melihat peningkatan kecepatan yang cukup besar. Akhirnya saya baru saja mengganti semua kata kunci INNER JOIN saya dengan STRAIGHT_JOIN untuk kueri ini dan sekarang berjalan dalam 0,01 detik.

Pertanyaan saya adalah kapan Anda menggunakan STRAIGHT_JOIN dan kapan Anda menggunakan INNER JOIN? Apakah ada alasan untuk tidak menggunakan STRAIGHT_JOIN jika Anda menulis kueri yang bagus?

Greg
sumber

Jawaban:

73

Saya tidak akan merekomendasikan penggunaan STRAIGHT_JOIN tanpa alasan yang kuat. Pengalaman saya sendiri adalah bahwa pengoptimal kueri MySQL lebih sering memilih rencana kueri yang buruk daripada yang saya inginkan, tetapi tidak cukup sering sehingga Anda harus memintasnya secara umum, yang akan Anda lakukan jika selalu menggunakan STRAIGHT_JOIN.

Rekomendasi saya adalah membiarkan semua pertanyaan sebagai GABUNG biasa. Jika Anda menemukan bahwa satu kueri menggunakan rencana kueri sub-optimal, saya sarankan pertama-tama mencoba menulis ulang atau menyusun ulang kueri sedikit untuk melihat apakah pengoptimal kemudian akan memilih rencana kueri yang lebih baik. Juga, setidaknya untuk innodb, pastikan tidak hanya statistik indeks Anda sudah kedaluwarsa ( TABEL ANALISIS ). Itu dapat menyebabkan pengoptimal memilih rencana kueri yang buruk. Petunjuk pengoptimal umumnya harus menjadi pilihan terakhir Anda.

Alasan lain untuk tidak menggunakan petunjuk kueri adalah karena distribusi data Anda dapat berubah seiring waktu, atau selektivitas indeks Anda dapat berubah, dll. Saat tabel Anda berkembang. Petunjuk kueri Anda yang optimal sekarang, mungkin menjadi kurang optimal seiring waktu. Tetapi pengoptimal tidak akan dapat menyesuaikan rencana kueri karena petunjuk Anda yang sekarang sudah usang. Anda tetap lebih fleksibel jika Anda mengizinkan pengoptimal membuat keputusan.

nathan
sumber
60
Jawaban ini sebenarnya tidak menjelaskan kapan harus digunakan straight_join .
Pacerier
23

Dari referensi MySQL JOIN :

"STRAIGHT_JOIN mirip dengan JOIN, hanya saja tabel kiri selalu dibaca sebelum tabel kanan. Ini dapat digunakan untuk (beberapa) kasus di mana pengoptimal gabungan meletakkan tabel dalam urutan yang salah."

jjclarkson.dll
sumber
28
Terima kasih, tapi saya sudah membaca manual MySQL di dalamnya. Berharap penjelasan lebih lanjut.
Greg
20

Berikut skenario yang muncul baru-baru ini di tempat kerja.

Pertimbangkan tiga tabel, A, B, C.

A memiliki 3.000 baris; B memiliki 300.000.000 baris; dan C memiliki 2.000 baris.

Kunci asing didefinisikan: B (a_id), B (c_id).

Misalkan Anda memiliki kueri yang terlihat seperti ini:

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

Dalam pengalaman saya, MySQL dapat memilih untuk pergi ke C -> B -> A dalam kasus ini. C lebih kecil dari A dan B sangat besar, dan semuanya equijoins.

Masalahnya adalah MySQL tidak selalu memperhitungkan ukuran persimpangan antara (C.id dan B.c_id) vs (A.id dan B.a_id). Jika gabungan antara B dan C menghasilkan baris sebanyak B, maka itu adalah pilihan yang sangat buruk; jika memulai dengan A akan memfilter B ke baris sebanyak A, maka itu akan menjadi pilihan yang jauh lebih baik. straight_joindapat digunakan untuk memaksa perintah ini seperti ini:

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

Sekarang aharus bergabung dulu b.

Umumnya Anda ingin melakukan penggabungan dalam urutan yang meminimalkan jumlah baris dalam set yang dihasilkan. Jadi memulai dengan meja kecil dan menggabungkan sehingga gabungan yang dihasilkan juga akan kecil, sangat ideal. Hal-hal menjadi buah pir jika dimulai dengan meja kecil dan menggabungkannya ke meja yang lebih besar akhirnya menjadi sebesar meja besar.

Ini tergantung statistik. Jika distribusi data berubah, kalkulasi dapat berubah. Ini juga tergantung pada detail implementasi dari mekanisme gabungan.

Kasus terburuk yang pernah saya lihat untuk MySQL yang semuanya kecuali diperlukan straight_joinatau petunjuk indeks agresif adalah kueri yang memberi nomor pada banyak data dalam urutan ketat dengan penyaringan cahaya. MySQL sangat memilih untuk menggunakan indeks untuk filter apa pun dan menggabungkannya; ini masuk akal karena kebanyakan orang tidak mencoba mengurutkan seluruh database tetapi memiliki subset baris terbatas yang responsif terhadap kueri, dan mengurutkan subset terbatas jauh lebih cepat daripada memfilter seluruh tabel, tidak peduli apakah itu diurutkan atau tidak. Dalam hal ini, menempatkan gabungan langsung segera setelah tabel yang memiliki kolom terindeks yang ingin saya sortir pada hal-hal yang diperbaiki.

Barry Kelly
sumber
Bagaimana Anda akan menggunakan gabungan langsung untuk memperbaiki masalah?
Hannele
@Hannele straight_joinmengevaluasi tabel kiri sebelum kanan. Jadi jika Anda ingin beralih dari A -> B -> Ccontoh saya, joinkata kunci pertama dapat diganti dengan straight_join.
Barry Kelly
Ah rapi. Akan berguna untuk memasukkannya sebagai contoh dalam jawaban Anda :)
Hannele
18

MySQL tidak harus pandai memilih urutan gabungan dalam kueri yang kompleks. Dengan menetapkan kueri kompleks sebagai straight_join, kueri akan mengeksekusi gabungan dalam urutan yang ditentukan. Dengan menempatkan tabel menjadi penyebut paling tidak umum terlebih dahulu dan menentukan straight_join, Anda dapat meningkatkan kinerja kueri.

IAdapter
sumber
11

STRAIGHT_JOIN, dengan menggunakan klausa ini, Anda dapat mengontrol JOINurutan: tabel mana yang dipindai di loop luar dan tabel mana yang berada di loop dalam.

Mitendra
sumber
Apa loop luar dan loop dalam?
Istiaque Ahmed
Tabel @IstiaqueAhmed digabungkan dengan loop bersarang (ambil baris pertama dari tabel A dan loop melempar tabel B lalu ambil baris kedua ... dan seterusnya. Berikut tabel A di loop luar)
Akuntan م
6

Saya akan memberi tahu Anda mengapa saya harus menggunakan STRAIGHT_JOIN:

  • Saya mengalami masalah kinerja dengan sebuah kueri.
  • Menyederhanakan kueri, kueri itu tiba-tiba menjadi lebih efisien
  • Mencoba mencari tahu bagian mana yang menyebabkan masalah, saya tidak bisa. (2 kiri bergabung bersama lambat, dan masing-masing cepat independen)
  • Saya kemudian mengeksekusi EXPLAIN dengan kueri lambat dan cepat (tambahkan salah satu dari gabungan kiri)
  • Anehnya, MySQL mengubah sepenuhnya perintah JOIN antara 2 kueri.

Oleh karena itu saya paksa salah satu join menjadi straight_join untuk FORCE join sebelumnya dibaca dulu. Ini mencegah MySQL untuk mengubah urutan eksekusi dan bekerja dengan sangat baik!

Nicolas Thery
sumber
2

Dalam pengalaman singkat saya, salah satu situasi yang STRAIGHT_JOINtelah mengurangi kueri saya dari 30 detik menjadi 100 milidetik adalah bahwa tabel pertama dalam rencana eksekusi bukanlah tabel yang diurutkan berdasarkan kolom

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

JIKA pengoptimal memilih untuk memukul stores lebih dulu, itu akan menyebabkan Using index; Using temporary; Using filesortkarena

jika ORDER BY atau GROUP BY berisi kolom dari tabel selain tabel pertama dalam antrian gabungan, tabel sementara akan dibuat.

sumber

di sini pengoptimal membutuhkan sedikit bantuan dengan menyuruhnya menekan saleslebih dulu menggunakan

sales STRAIGHT_JOIN stores
Akuntan م
sumber
1
(Saya memperindah Jawaban Anda.)
Rick James
2

Jika permintaan Anda berakhir dengan ORDER BY... LIMIT..., itu mungkin optimal untuk merumuskan query untuk mengelabui optimizer ke melakukan LIMIT sebelum itu JOIN.

(Jawaban ini tidak hanya berlaku untuk pertanyaan awal tentang STRAIGHT_JOIN, juga tidak berlaku untuk semua kasus STRAIGHT_JOIN.)

Dimulai dengan contoh oleh @Accountant م , ini seharusnya berjalan lebih cepat dalam banyak situasi. (Dan itu menghindari petunjuk yang dibutuhkan.)

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;

Catatan:

  • Pertama, 50 id diambil. Ini akan menjadi sangat cepat dengan INDEX(date, id).
  • Kemudian bergabung kembali ke salesmemungkinkan Anda mendapatkan hanya 50 "whatevers" tanpa menyeretnya ke dalam tabel temp.
  • karena subkueri, menurut definisi, tidak berurutan, ORDER BYharus diulangi di kueri luar. (Pengoptimal mungkin menemukan cara untuk menghindari benar-benar melakukan penyortiran lain.)
  • Ya, ini lebih berantakan. Tapi biasanya lebih cepat.

Saya menentang penggunaan hit karena "Meskipun hari ini lebih cepat, mungkin gagal lebih cepat besok."

Rick James
sumber
0

Saya tahu ini agak tua tetapi inilah skenarionya, saya telah melakukan skrip batch untuk mengisi tabel tertentu. Pada titik tertentu, kueri berjalan sangat lambat. Tampaknya urutan gabungan salah pada catatan tertentu:

  • Dalam urutan yang benar

masukkan deskripsi gambar di sini

  • Menambahkan id sebanyak 1 akan mengacaukan pesanan. Perhatikan bidang 'Extra'

masukkan deskripsi gambar di sini

  • Menggunakan straight_join memperbaiki masalah

masukkan deskripsi gambar di sini

Urutan yang salah berjalan sekitar 65 detik sementara penggunaan straight_join berjalan dalam milidetik

rai
sumber
-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
lhs295988029
sumber
3
Ini tidak memberi Anda informasi yang cukup untuk mengetahui kapan gabungan lurus sesuai.
Hannele