日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 运维知识 > 数据库 >内容正文

数据库

【explain】MySQL联表查询中的驱动表

發(fā)布時(shí)間:2025/3/8 数据库 21 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【explain】MySQL联表查询中的驱动表 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

寫在前面

1、不要求每個(gè)人一定理解 聯(lián)表查詢(join/left join/inner join等)時(shí)的mysql運(yùn)算過(guò)程

2、不要求每個(gè)人一定知道線上(現(xiàn)在或未來(lái))哪張表數(shù)據(jù)量大,哪張表數(shù)據(jù)量小

3、但把mysql客戶端(如SQLyog,如HeidiSQL)放在桌面上,時(shí)不時(shí)拿出來(lái) explain 一把,這是一種美德!

在實(shí)例講解之前,我們先回顧一下聯(lián)表查詢的基礎(chǔ)知識(shí)

聯(lián)表查詢的基礎(chǔ)知識(shí)

引子:為什么第一個(gè)查詢using temporary,第二個(gè)查詢不用臨時(shí)表呢?

下面兩個(gè)查詢,它們只差了一個(gè)order by,效果卻迥然不同。

第一個(gè)查詢:

EXPLAIN extended SELECT ads.id FROM ads, city WHEREcity.city_id = 8005AND ads.status = 'online'AND city.ads_id=ads.id ORDER BY ads.id desc

執(zhí)行計(jì)劃為:

id select_type table type possible_keys key key_len ref rows filtered Extra 1
SIMPLE city ref ads_id,city_id city_id 4 const 2838 100.00 Using temporary; Using filesort 1
SIMPLE ads eq_ref PRIMARY PRIMARY 4 city.ads_id 1 100.00 Using where

第二個(gè)查詢:

EXPLAIN extended SELECT ads.id FROM ads,city WHEREcity.city_id =8005AND ads.status = 'online'AND city.ads_id=ads.id ORDER BY city.ads_id desc

執(zhí)行計(jì)劃里沒(méi)有了using temporary:

id select_type table type possible_keys key key_len ref rows filtered Extra 1
SIMPLE city ref ads_id,city_id city_id 4 const 2838 100.00 Using where; Using filesort 1
SIMPLE ads eq_ref PRIMARY PRIMARY 4 city.ads_id 1 100.00 Using where

為什么?DBA告訴我們:

MySQL 表關(guān)聯(lián)的算法是 Nest Loop Join,是通過(guò)驅(qū)動(dòng)表的結(jié)果集作為循環(huán)基礎(chǔ)數(shù)據(jù),然后一條一條地通過(guò)該結(jié)果集中的數(shù)據(jù)作為過(guò)濾條件到下一個(gè)表中查詢數(shù)據(jù),然后合并結(jié)果。

EXPLAIN 結(jié)果中,第一行出現(xiàn)的表就是驅(qū)動(dòng)表(Important!)

以上兩個(gè)查詢語(yǔ)句,驅(qū)動(dòng)表都是 city,如上面的執(zhí)行計(jì)劃所示!

對(duì)驅(qū)動(dòng)表可以直接排序,對(duì)非驅(qū)動(dòng)表(的字段排序)需要對(duì)循環(huán)查詢的合并結(jié)果(臨時(shí)表)進(jìn)行排序(Important!)

因此,order by ads.id desc 時(shí),就要先 using temporary 了!

驅(qū)動(dòng)表的定義

wwh999?在 2006年總結(jié)說(shuō),當(dāng)進(jìn)行多表連接查詢時(shí), [驅(qū)動(dòng)表] 的定義為:

1)指定了聯(lián)接條件時(shí),滿足查詢條件的記錄行數(shù)少的表為[驅(qū)動(dòng)表]

2)未指定聯(lián)接條件時(shí),行數(shù)少的表為[驅(qū)動(dòng)表](Important!)

忠告:如果你搞不清楚該讓誰(shuí)做驅(qū)動(dòng)表、誰(shuí) join 誰(shuí),請(qǐng)讓 MySQL 運(yùn)行時(shí)自行判斷

既然“未指定聯(lián)接條件時(shí),行數(shù)少的表為[驅(qū)動(dòng)表]”了,而且你也對(duì)自己寫出的復(fù)雜的 Nested Loop Join 不太有把握(如下面的實(shí)例所示),就別指定誰(shuí) left/right join 誰(shuí)了,請(qǐng)交給 MySQL優(yōu)化器 運(yùn)行時(shí)決定吧。

如果您對(duì)自己特別有信心,可以像火丁一樣做優(yōu)化。

小結(jié)果集驅(qū)動(dòng)大結(jié)果集

de.cel 在2012年總結(jié)說(shuō),不管是你,還是 MySQL,優(yōu)化的目標(biāo)是盡可能減少JOIN中Nested Loop的循環(huán)次數(shù)。

以此保證:永遠(yuǎn)用小結(jié)果集驅(qū)動(dòng)大結(jié)果集(Important!)!

實(shí)例講解

Nested Loop Join慢查SQL語(yǔ)句

先了解一下 mb 表有 千萬(wàn)級(jí)記錄,mbei 表要少得多。慢查實(shí)例如下:

explain SELECT mb.id, …… FROMmb LEFT JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid WHERE 1=1 ORDER BY mbei.apply_time DESC limit 0,10

夠復(fù)雜吧。Nested Loop Join 就是這樣,以驅(qū)動(dòng)表的結(jié)果集作為循環(huán)的基礎(chǔ)數(shù)據(jù),然后將結(jié)果集中的數(shù)據(jù)作為過(guò)濾條件一條條地到下一個(gè)表中查詢數(shù)據(jù),最后合并結(jié)果。

此時(shí)還有第三個(gè)表,則將前兩個(gè)表的 Join 結(jié)果集作為循環(huán)基礎(chǔ)數(shù)據(jù),再一次通過(guò)循環(huán)查詢條件到第三個(gè)表中查詢數(shù)據(jù),如此反復(fù)。

這條語(yǔ)句的執(zhí)行計(jì)劃如下:

id select_type table type possible_keys key key_len ref rows Extra 1
SIMPLE mb index userid userid 4 (NULL) 6060455 Using index; Using temporary; Using filesort 1
SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1 1
SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

由于動(dòng)用了“LEFT JOIN”,所以攻城獅已經(jīng)指定了驅(qū)動(dòng)表,雖然這張驅(qū)動(dòng)表的結(jié)果集記錄數(shù)達(dá)到百萬(wàn)級(jí)!

如何優(yōu)化?

優(yōu)化第一步:LEFT JOIN改為JOIN,干嘛要 left join 啊?直接 join!

explain SELECT mb.id…… FROM mb JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid WHERE 1=1 ORDER BY mbei.apply_time DESC limit 0,10

立竿見(jiàn)影,驅(qū)動(dòng)表立刻變?yōu)樾”?mbei 了, Using temporary 消失了,影響行數(shù)少多了:

id select_type table type possible_keys key key_len ref rows Extra 1
SIMPLE mbei ALL mb_id (NULL) (NULL) (NULL) 13383 Using filesort 1
SIMPLE mb eq_ref PRIMARY,userid PRIMARY 4 mbei.mb_id 1 1
SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

優(yōu)化第一步之分支1:根據(jù)驅(qū)動(dòng)表的字段排序,好嗎?

left join不變。干嘛要根據(jù)非驅(qū)動(dòng)表的字段排序呢?我們前面說(shuō)過(guò)“對(duì)驅(qū)動(dòng)表可以直接排序,對(duì)非驅(qū)動(dòng)表(的字段排序)需要對(duì)循環(huán)查詢的合并結(jié)果(臨時(shí)表)進(jìn)行排序!”的。

explain SELECT mb.id…… FROM mb LEFT JOIN mbei ON mb.id=mbei.mb_id INNER JOINu ON mb.uid=u.uid WHERE 1=1 ORDER BY mb.id DESC limit 0,10

也滿足業(yè)務(wù)場(chǎng)景,做到了rows最小:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE mb index userid PRIMARY 4 (NULL) 10
1 SIMPLE mbei eq_ref mb_id mb_id 4 mb.id 1 Using index
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

優(yōu)化第二步:去除所有JOIN,讓MySQL自行決定!寫這么多密密麻麻的 left join/inner join 很開心嗎?

explain
SELECT mb.id……
FROM mb,mbei,u
WHERE
mb.id=mbei.mb_id
and mb.uid=u.user_id
order by mbei.apply_time desc
limit 0,10

立竿見(jiàn)影,驅(qū)動(dòng)表一樣是小表 mbei:

id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE mbei ALL mb_id (NULL) (NULL) (NULL) 13388 Using filesort
1 SIMPLE mb eq_ref PRIMARY,userid PRIMARY 4 mbei.mb_id 1
1 SIMPLE u eq_ref PRIMARY PRIMARY 4 mb.uid 1 Using index

最后的總結(jié):

強(qiáng)調(diào)再?gòu)?qiáng)調(diào):

1、不要過(guò)于相信你的運(yùn)氣!

2、不要相信你的開發(fā)環(huán)境里SQL的執(zhí)行速度!

3、請(qǐng)拿起 explain 武器,如果你看到以下現(xiàn)象,請(qǐng)優(yōu)化:

1)出現(xiàn)了Using temporary

2)rows過(guò)多,或者幾乎是全表的記錄數(shù)

3)key 是 (NULL)

4)possible_keys 出現(xiàn)過(guò)多(待選)索引

記住,explain 是一種美德!

與50位技術(shù)專家面對(duì)面20年技術(shù)見(jiàn)證,附贈(zèng)技術(shù)全景圖

總結(jié)

以上是生活随笔為你收集整理的【explain】MySQL联表查询中的驱动表的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。