2018-09-29 一次left join導(dǎo)致的性能問題

背景說明

基礎(chǔ)環(huán)境介紹

  • 應(yīng)用環(huán)境: 基于springcloud的java應(yīng)用
  • 數(shù)據(jù)庫:mysql
  • DAO: mybatis+pagehelper

主要庫表結(jié)構(gòu)

  • leads_info 銷售線索主表拣帽,數(shù)據(jù)量約40萬條,本次涉及的關(guān)鍵字段:
    • id 線索編號(hào)
    • phone_no 客戶電話號(hào)碼
    • cust_name 客戶姓名
    • first_duplicate_id 本線索第一條重復(fù)線索的id
    • last_duplicate_id 本線索的最后一條重復(fù)線索
    • enquire_date 線索留資時(shí)間
  • cust_base_info 若客戶姓名有修改記錄嚼锄,數(shù)據(jù)量約1300條
    • id 修改記錄id
    • leads_id 線索id
    • name 客戶姓名

主要業(yè)務(wù)邏輯:

  1. 重復(fù)線索:若有同樣電話的銷售線索减拭,則新插入一條數(shù)據(jù),將新插入的數(shù)據(jù)的first_group_id設(shè)置為第一條同樣號(hào)碼的線索区丑;第一條線索的last_group_id設(shè)置為最新的一條線索id
  2. 修改客戶姓名:
    1. 在cust_base_info中查找對(duì)應(yīng)線索id的數(shù)據(jù)拧粪,若有則更新數(shù)據(jù),若無則插入一條數(shù)據(jù)沧侥;
    2. 若此線索為重復(fù)線索可霎,則所有修改只針對(duì)其first_group_id來做

PS:再次吐槽這樣的設(shè)計(jì)

問題發(fā)現(xiàn)

問題癥狀

原來系統(tǒng)在執(zhí)行的過程中,就發(fā)現(xiàn)系統(tǒng)在列表與分頁的過程中宴杀,發(fā)現(xiàn)系統(tǒng)比較慢癣朗,但是還可以接受,后來發(fā)現(xiàn)系統(tǒng)越來越慢旺罢,直至導(dǎo)致springcloud超時(shí)(我們?cè)O(shè)置了15秒的超長(zhǎng)超時(shí)時(shí)間)

關(guān)鍵sql

經(jīng)過查詢?nèi)罩究跤啵l(fā)現(xiàn)出現(xiàn)問題的列表sql如下

select
    a.id as "id",
    a.site_id as "siteId",
    a.name as "name",
        ……
    c.name as "custBaseInfo.name"
from
    leads_info a
left join sys_user b on
    a.next_track_user = b.id
left join cust_base_info c on
    (a.id = c.leads_id
          or a.first_duplicate_id = c.leads_id)
    and c.del_flag = 0
WHERE
    a.del_flag = 0
    and a.site_id = ?
    and (a.last_duplicate_id is null
    or a.last_duplicate_id is not null
    and a.id = a.last_duplicate_id)
order by
    a.enquire_date desc
LIMIT 20
  • where條件主要是為了只取最新的一條數(shù)據(jù)
  • join的條件是為了保證在新線索來以前修改的客戶姓名

問題分析與解決

第一步,分析leads_info

發(fā)現(xiàn)leads_info表除了id是primary key以外扁达,無任何索引正卧,先給他加上索引

  • 索引一
    • site_id
    • enquire_date
  • 索引二
    • site_id
    • last_duplicate_id
    • enquire_date

添加索引后,發(fā)現(xiàn)速度有所提升罩驻,但是并沒有明顯的提升

第二部穗酥,分析join表

首先去掉left join cust_base_info,執(zhí)行下面的sql

select
    a.id as "id",
    a.site_id as "siteId",
    a.name as "name",
        ……
    c.name as "custBaseInfo.name"
from
    leads_info a
left join sys_user b on
    a.next_track_user = b.id
WHERE
    a.del_flag = 0
    and a.site_id = ?
    and (a.last_duplicate_id is null
    or a.last_duplicate_id is not null
    and a.id = a.last_duplicate_id)
order by
    a.enquire_date desc
LIMIT 20

發(fā)現(xiàn)速度飛快惠遏,可以確定是這個(gè)join的問題

查詢cust_base_info表砾跃,發(fā)現(xiàn)此表的id是主鍵,leads_id無索引节吮,left join可能存在一對(duì)多抽高,對(duì)此表的leads_id設(shè)置為unique索引

建立索引后,發(fā)現(xiàn)上述sql性能有了飛速的提升透绩,在dbeaver中查詢時(shí)間降低到了ms級(jí)別翘骂,但是在生產(chǎn)系統(tǒng)修改了庫表以后壁熄,發(fā)現(xiàn)前端展示仍未能解決,相應(yīng)時(shí)間有所降低碳竟,但是仍然無法忍受草丧,需要等待將近10s

最終解決

繼續(xù)查詢系統(tǒng)日志,發(fā)現(xiàn)因?yàn)榉猪摰男枰ㄎΓ趫?zhí)行列表查詢以前昌执,系統(tǒng)需要首先做一個(gè)count的操作,具體sql如下:

select
    count(1)
from
    leads_info a
left join sys_user b on
    a.next_track_user = b.id
left join cust_base_info c on
    (a.id = c.leads_id
          or a.first_duplicate_id = c.leads_id)
    and c.del_flag = 0

此sql的響應(yīng)時(shí)間為大概9s诈泼,找到原因懂拾,發(fā)現(xiàn)就出在了left join cust_base_info的條件上,因?yàn)槔锩嬗衞r條件铐达,故此mysql在執(zhí)行的時(shí)候因?yàn)闊o法保證left join不增加數(shù)據(jù)條數(shù)岖赋,所以需要把所有的數(shù)據(jù)全部完成join以后才能進(jìn)行count統(tǒng)計(jì),果斷將這個(gè)條件作了修改:

select
    a.id as "id",
    a.site_id as "siteId",
    a.name as "name",
        ……
    c.name as "custBaseInfo.name"
from
    leads_info a
left join sys_user b on
    a.next_track_user = b.id
left join cust_base_info c on
    ifnull(a.fist_duplicate_id, a.id) = c.leads_id
    and c.del_flag = 0
WHERE
    a.del_flag = 0
    and a.site_id = ?
    and (a.last_duplicate_id is null
    or a.last_duplicate_id is not null
    and a.id = a.last_duplicate_id)
order by
    a.enquire_date desc

修改后瓮孙,系統(tǒng)性能得到飛速提升唐断,列表頁面響應(yīng)時(shí)間可以控制在1s以內(nèi)了。

總結(jié)

總結(jié)本次問題衷畦,得出要想在分頁時(shí)得到最佳的性能栗涂,總原則如下:

  1. 通過主表(本例中的leads_info)與從表(本例中的cust_base_info)關(guān)鍵,要盡可能的使用left join操作祈争,少用inner join斤程,避免使用outer join
  2. 主表與其他表left join的條件最好是主表的某一個(gè)字段去關(guān)聯(lián)主表的主鍵或unique的屬性值
  3. 關(guān)聯(lián)條件中不要有or條件
  4. 查詢條件集中在主表上,盡量不要涉及從表的字段菩混;唯一可以有的是判斷是否關(guān)聯(lián)上忿墅,本例中可以用 where b.leads_is is null,對(duì)于必須關(guān)鍵上的條件沮峡,可以寫成 inner join
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末疚脐,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子邢疙,更是在濱河造成了極大的恐慌棍弄,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,718評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件疟游,死亡現(xiàn)場(chǎng)離奇詭異呼畸,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)颁虐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,683評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門蛮原,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人另绩,你說我怎么就攤上這事儒陨』ㄋ唬” “怎么了?”我有些...
    開封第一講書人閱讀 158,207評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵蹦漠,是天一觀的道長(zhǎng)椭员。 經(jīng)常有香客問我,道長(zhǎng)笛园,這世上最難降的妖魔是什么拆撼? 我笑而不...
    開封第一講書人閱讀 56,755評(píng)論 1 284
  • 正文 為了忘掉前任,我火速辦了婚禮喘沿,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘竭贩。我一直安慰自己蚜印,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,862評(píng)論 6 386
  • 文/花漫 我一把揭開白布留量。 她就那樣靜靜地躺著窄赋,像睡著了一般。 火紅的嫁衣襯著肌膚如雪楼熄。 梳的紋絲不亂的頭發(fā)上忆绰,一...
    開封第一講書人閱讀 50,050評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音可岂,去河邊找鬼错敢。 笑死,一個(gè)胖子當(dāng)著我的面吹牛缕粹,可吹牛的內(nèi)容都是我干的稚茅。 我是一名探鬼主播,決...
    沈念sama閱讀 39,136評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼平斩,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼亚享!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起绘面,我...
    開封第一講書人閱讀 37,882評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤欺税,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后揭璃,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體晚凿,經(jīng)...
    沈念sama閱讀 44,330評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,651評(píng)論 2 327
  • 正文 我和宋清朗相戀三年塘辅,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晃虫。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,789評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扣墩,死狀恐怖哲银,靈堂內(nèi)的尸體忽然破棺而出扛吞,到底是詐尸還是另有隱情,我是刑警寧澤荆责,帶...
    沈念sama閱讀 34,477評(píng)論 4 333
  • 正文 年R本政府宣布滥比,位于F島的核電站,受9級(jí)特大地震影響做院,放射性物質(zhì)發(fā)生泄漏盲泛。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,135評(píng)論 3 317
  • 文/蒙蒙 一键耕、第九天 我趴在偏房一處隱蔽的房頂上張望寺滚。 院中可真熱鬧,春花似錦屈雄、人聲如沸村视。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,864評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蚁孔。三九已至,卻和暖如春惋嚎,著一層夾襖步出監(jiān)牢的瞬間杠氢,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,099評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工另伍, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留鼻百,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,598評(píng)論 2 362
  • 正文 我出身青樓质况,卻偏偏與公主長(zhǎng)得像愕宋,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子结榄,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,697評(píng)論 2 351

推薦閱讀更多精彩內(nèi)容