MySQL Left Join(左連接) 耗時(shí)嚴(yán)重的問題

現(xiàn)象:列表頁因超時(shí)查不出來東西,使用postman模擬請花費(fèi)40多秒,將sql語句單獨(dú)提出來后查詢速度非常慢,40多秒
先上結(jié)論:
在兩個(gè)表關(guān)聯(lián)字段上建立索引解決此問題,下面的內(nèi)容比這句話爽多了,請繼續(xù)看
表結(jié)構(gòu)如下:
users(用戶)表:id,name
integal_record(分?jǐn)?shù)記錄)表:id,user_id,integal_id
其中,integal_record表的user_id關(guān)聯(lián)著users表的id,業(yè)務(wù)目的是查詢每個(gè)員工閱讀次數(shù)、評(píng)論次數(shù)和積分總和,查詢語句如下:
(里面寫死的值是我從mybatis里扣出來的)
integral_id=1代表著閱讀,integral_id=2代表著評(píng)論

SELECT u.`name`
    , IF(SUM(ir.integral_id = 1) > 0, SUM(ir.integral_id = 1), 0) AS 'read'
    , IF(SUM(ir.integral_id = 2) > 0, SUM(ir.integral_id = 2), 0) AS 'comment'
    , IF(SUM(ir.integral_id = 1) > 0, SUM(ir.integral_id = 1), 0) + IF(SUM(ir.integral_id = 2) > 0, SUM(ir.integral_id = 2), 0) AS count
FROM users u
    LEFT JOIN integral_record ir ON ir.user_id = u.id
WHERE u.role_code = 1
    AND u.actived = 1
    AND u.deleted_at > now()
    AND u.network_id = 29
    AND u.id NOT IN (
        SELECT user_id
        FROM roles
        WHERE role_item_id = 7
    )
GROUP BY u.id
ORDER BY ifnull(SUM(ir.integral_id = 1), 0) + ifnull(SUM(ir.integral_id = 2), 0) DESC, u.pinyin ASC;
共11644條 查詢時(shí)間:27.976s

查詢時(shí)間快半分鐘是無法讓人接受的,從表象的SQL語句來說,首先我們看到有很多SUM函數(shù),因此我們來測試下SUM函數(shù)的計(jì)算時(shí)間,如下查詢每個(gè)人閱讀的累計(jì)次數(shù):

select SUM(integral_id=1) as 'count' from integral_record group by user_id;
共13000條 查詢時(shí)間:0.084s

可以看出來計(jì)算時(shí)間很長和SUM函數(shù)沒什么關(guān)系,再看整個(gè)SQL除了一些SUM函數(shù)外就剩下users表左聯(lián)integal_record表,我們把條件去掉,直接就寫個(gè)左查詢試試

select * from  users u LEFT JOIN integral_record mtemp on u.id =mtemp.user_id;
共13001條 查詢時(shí)間:69.919s

amazing,我的users表里有13400條數(shù)據(jù),關(guān)聯(lián)的integral_record 表里也有13000條數(shù)據(jù),僅僅做了左連接竟然花費(fèi)了70秒,這肯定是超時(shí)的元兇;所以我們explain一下,看看mysql對(duì)這條數(shù)據(jù)的查詢策略:

EXPLAIN select * from  users u LEFT JOIN integral_record mtemp on u.id =mtemp.user_id;

查看結(jié)果:


1550470363(1).jpg

我們看到type字段的結(jié)果是All,也就是代表全表掃描,那么就好辦了,建立索引即可,其中users表中的id屬于主鍵,策略自增,有默認(rèn)的索引,不在考慮范圍內(nèi),我們僅需對(duì)integral_record表增加索引即可:

alter table integral_record  add index user_id_index(user_id)

繼續(xù)執(zhí)行左聯(lián)語句查看運(yùn)行時(shí)間:

select * from  users u LEFT JOIN integral_record mtemp on u.id =mtemp.user_id;
共13406條 查詢時(shí)間:67.549s

時(shí)間仍然很久,索引并沒有起作用,這里排除一些逃避困難時(shí)的迷信想法:mysql有bug 或者navicat有bug,有網(wǎng)絡(luò)問題 哈哈哈.....我們來看左聯(lián)的關(guān)鍵屬性:


索引.jpg
user_id竟然是varchar的 ,但是users表中的id是int,這就造成了left join on users.id =integral_record.user_id時(shí)有類型轉(zhuǎn)換的問題而不能使用索引,因此,我們把user_id更改成int,再看看時(shí)間....(盡管兩三句就寫完了,這個(gè)數(shù)據(jù)類型煩擾了我整整找了一上午去找)
select * from  users u LEFT JOIN integral_record mtemp on u.id =mtemp.user_id;
共13406條 查詢時(shí)間:0.375s

執(zhí)行時(shí)間從60多秒變成了0.375s,整個(gè)世界都安靜了,再次explain一下

EXPLAIN select * from  users u LEFT JOIN integral_record mtemp on u.id =mtemp.user_id;
explain改.jpg

發(fā)現(xiàn)mtemp(integal)表的檢索方式從all變成了ref,索引,快的一批,深呼吸....

后記

希望繼續(xù)遇到這種sql需要優(yōu)化的問題來增加sql調(diào)優(yōu)相關(guān)經(jīng)驗(yàn)

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

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

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