Mysql優(yōu)化 -- group by

背景

由于整體的歷史原因,導(dǎo)致部分設(shè)備存在COMMON類型無(wú)法進(jìn)行區(qū)分,需要根據(jù)對(duì)應(yīng)的通道數(shù)進(jìn)行區(qū)分,導(dǎo)致需要device 和camera的兩個(gè)表需要級(jí)聯(lián)查詢,進(jìn)行分頁(yè)操作。

未優(yōu)化前SQL語(yǔ)句為:

SELECT a.serial_uuid, a.user_id, a.device_name
FROM device a
         LEFT JOIN camera b
                   USING (serial_uuid)
WHERE a.user_id = 1
GROUP BY b.serial_uuid
HAVING count(b.serial_uuid) > 1
LIMIT 0, 10;
;

device表的索引

  PRIMARY KEY (`id`),
  UNIQUE KEY `uidx_device_serial` (`serial_uuid`),
  KEY `idx_user_id` (`user_id`) 

camera表的索引

  UNIQUE KEY `uidx_serial_channel` (`serial_uuid`,`channel_no`),
  KEY `idx_user_id` (`user_id`)

我們來(lái)explain一下這個(gè)sql語(yǔ)句

image.png

這里看到索引是有的,但是IP攻擊次數(shù)表device 也用上了臨時(shí)表。那么這SQL不優(yōu)化直接第一次執(zhí)行需要多久(這里強(qiáng)調(diào)第一次是因?yàn)镸YSQL帶有緩存功能,執(zhí)行過(guò)一次的同樣SQL,第二次會(huì)快很多。)

查詢時(shí)間達(dá)到1s中左右
那么我們?cè)趺磧?yōu)化呢,索引既然走了,我嘗試一下避免臨時(shí)表,這時(shí)我們先了解一下臨時(shí)表跟group by的使聯(lián)系:

查找了網(wǎng)上一些博客分析GROUP BY 與臨時(shí)表的關(guān)系 :

  1. 如果GROUP BY 的列沒(méi)有索引,產(chǎn)生臨時(shí)表.
  2. 如果GROUP BY時(shí),SELECT的列不止GROUP BY列一個(gè),并且GROUP BY的列不是主鍵 ,產(chǎn)生臨時(shí)表.
  3. 如果GROUP BY的列有索引,ORDER BY的列沒(méi)索引.產(chǎn)生臨時(shí)表.
  4. 如果GROUP BY的列和ORDER BY的列不一樣,即使都有索引也會(huì)產(chǎn)生臨時(shí)表.
  5. 如果GROUP BY或ORDER BY的列不是來(lái)自JOIN語(yǔ)句第一個(gè)表.會(huì)產(chǎn)生臨時(shí)表.
  6. 如果DISTINCT 和 ORDER BY的列沒(méi)有索引,產(chǎn)生臨時(shí)表.

ROWS的行數(shù)770W而且還是有臨時(shí)表,看來(lái)這復(fù)合索引也是不可取。
到此,避免臨時(shí)表方法失敗了,我們得從其他角度想想如何優(yōu)化。
其實(shí),9W的臨時(shí)表并不算多,那么為什么導(dǎo)致會(huì)這么久的查詢呢?我們想想這沒(méi)優(yōu)化的SQL的執(zhí)行過(guò)程是怎么樣的呢?

網(wǎng)上搜索得知內(nèi)聯(lián)表查詢一般的執(zhí)行過(guò)程是:
1、執(zhí)行FROM語(yǔ)句
2、執(zhí)行ON過(guò)濾
3、添加外部行
4、執(zhí)行where條件過(guò)濾
5、執(zhí)行g(shù)roup by分組語(yǔ)句
6、執(zhí)行having
7、select列表
8、執(zhí)行distinct去重復(fù)數(shù)據(jù)
9、執(zhí)行order by字句
10、執(zhí)行l(wèi)imit字句

這里得知,Mysql 是先執(zhí)行內(nèi)聯(lián)表然后再進(jìn)行條件查詢最后再分組,那么想想這SQL的條件查詢和分組都只是一個(gè)表的,內(nèi)聯(lián)后數(shù)據(jù)就變得臃腫了,這時(shí)候再進(jìn)行條件查詢和分組是否太吃虧了,我們可以嘗試一下提前進(jìn)行分組和條件查詢,實(shí)現(xiàn)方法就是子查詢聯(lián)合內(nèi)聯(lián)查詢。

group的也是 索引,根據(jù)之前group by的第一條如果GROUP BY 的列沒(méi)有索引,產(chǎn)生臨時(shí)表.
這里子查詢可以使用group利用serial_uuid的索引(不太清楚為什么級(jí)聯(lián)查詢沒(méi)有使用上),可以使用mysql默認(rèn)的b+tree 來(lái)避免排序分組,避免產(chǎn)生臨時(shí)表。

image.png

camera表的索引

  UNIQUE KEY `uidx_serial_channel` (`serial_uuid`,`channel_no`),
  KEY `idx_user_id` (`user_id`,`serial_uuid`)

增加索引后的explain


image.png

優(yōu)化后的sql語(yǔ)句為:

SELECT a.serial_uuid
FROM
    (SELECT
         b.id,
         b.dvc_device_id,
         b.serial_uuid
     FROM camera b
     WHERE b.user_id = 1
     GROUP BY b.serial_uuid
     HAVING COUNT(b.serial_uuid) > 1
    ) c
    LEFT JOIN device a
        ON c.serial_uuid = a.serial_uuid
WHERE a.user_id = 1
LIMIT 0, 10;

這樣查詢速度提升了到了97ms,提升了10倍。
要是有其它更好的方案希望指出進(jìn)行交流,來(lái)增加對(duì)mysql的學(xué)習(xí)和了解

總結(jié):

由于級(jí)聯(lián)查詢的中間表會(huì)非常大,最好能限制連的表大小的條件都先用上了,同時(shí)盡量讓條件查詢和分組執(zhí)行的表盡量小。

參考:https://blog.csdn.net/Tim_phper/article/details/78344444

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容