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?
straight_join
.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."
sumber
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_join
dapat 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
a
harus bergabung dulub
.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_join
atau 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.sumber
straight_join
mengevaluasi tabel kiri sebelum kanan. Jadi jika Anda ingin beralih dariA -> B -> C
contoh saya,join
kata kunci pertama dapat diganti denganstraight_join
.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.
sumber
STRAIGHT_JOIN
, dengan menggunakan klausa ini, Anda dapat mengontrolJOIN
urutan: tabel mana yang dipindai di loop luar dan tabel mana yang berada di loop dalam.sumber
Saya akan memberi tahu Anda mengapa saya harus menggunakan STRAIGHT_JOIN:
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!
sumber
Dalam pengalaman singkat saya, salah satu situasi yang
STRAIGHT_JOIN
telah 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 menyebabkanUsing index; Using temporary; Using filesort
karenasumber
di sini pengoptimal membutuhkan sedikit bantuan dengan menyuruhnya menekan
sales
lebih dulu menggunakansumber
Jika permintaan Anda berakhir dengan
ORDER BY... LIMIT...
, itu mungkin optimal untuk merumuskan query untuk mengelabui optimizer ke melakukanLIMIT
sebelum ituJOIN
.(Jawaban ini tidak hanya berlaku untuk pertanyaan awal tentang
STRAIGHT_JOIN
, juga tidak berlaku untuk semua kasusSTRAIGHT_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:
INDEX(date, id)
.sales
memungkinkan Anda mendapatkan hanya 50 "whatevers" tanpa menyeretnya ke dalam tabel temp.ORDER BY
harus diulangi di kueri luar. (Pengoptimal mungkin menemukan cara untuk menghindari benar-benar melakukan penyortiran lain.)Saya menentang penggunaan hit karena "Meskipun hari ini lebih cepat, mungkin gagal lebih cepat besok."
sumber
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:
Urutan yang salah berjalan sekitar 65 detik sementara penggunaan straight_join berjalan dalam milidetik
sumber
--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
sumber