<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          使用索引翻車了,差點(diǎn)被辭退

          共 3866字,需瀏覽 8分鐘

           ·

          2021-11-18 11:03

          前一陣子生產(chǎn)環(huán)境出現(xiàn)了接口響應(yīng)慢的問題,最終定位是由于一條慢 SQL 導(dǎo)致的。分析了一下這條慢 SQL,發(fā)現(xiàn)對(duì)應(yīng)數(shù)據(jù)庫表也加了索引,最終結(jié)論是某些場(chǎng)景下索引失效了。

          索引失效導(dǎo)致全表掃描,執(zhí)行速度非常慢,導(dǎo)致大量的 SQL 處于阻塞狀態(tài)。

          領(lǐng)導(dǎo):你好好反省一下,為啥出這么低級(jí)的問題。

          我:好的,今晚通宵整一篇總結(jié)出來,年底不讓我扛績(jī)效了吧?!

          領(lǐng)導(dǎo):……看你這篇文章點(diǎn)贊能不能超過 100

          我:……沒戲,他們喜歡白嫖

          下面總結(jié)一下使用索引翻車的常見名場(chǎng)面,希望能幫助到大家,拿個(gè)好年終獎(jiǎng)!

          使用索引翻車場(chǎng)景

          為方便演示,我們提前先建立一張數(shù)據(jù)庫表。新建一個(gè)用戶表,id 為主鍵,user_id 為唯一索引,name 為普通索引,address 為普通索引:

          CREATE?TABLE?`t_user`?(
          ??`id`?int?NOT?NULL?AUTO_INCREMENT,
          ??`name`?varchar(255)?DEFAULT?NULL,
          ??`age`?int?DEFAULT?NULL,
          ??`address`?varchar(255)?DEFAULT?NULL,
          ??`user_id`?int?DEFAULT?NULL,
          ??PRIMARY?KEY?(`id`),
          ??UNIQUE?KEY?`index_user_id`?(`user_id`)?USING?BTREE,
          ??KEY?`index_name`?(`name`)?USING?BTREE,
          ??KEY?`index_address`?(`address`)?USING?BTREE
          )?ENGINE=InnoDB;

          查詢條件包含or,可能導(dǎo)致索引失效

          (1)執(zhí)行一條簡(jiǎn)單的 SQL

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?user_id?=?123456;

          查看執(zhí)行計(jì)劃的結(jié)果,很顯然走了索引:


          (2)加一個(gè) or 條件,把 age 字段加上

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?user_id?=?123456?or?age?=?18;

          查看執(zhí)行計(jì)劃的結(jié)果,很顯然是全表掃描:


          分析結(jié)論:

          • 對(duì)于條件中出現(xiàn) or 的情況,user_id 列加了索引,age 列是沒有加索引的,假設(shè) MySQL 一定要走索引,可能需要三步:索引掃描+全表掃描+合并;
          • 如果它一開始就走全表掃描,直接一遍掃描就完事了;
          • mysql是有優(yōu)化器的,處于效率與成本,遇到 or條件,索引可能失效,看起來也合情合理。

          敲黑板: 上面說的這種情況索引有可能失效,沒有說一定會(huì)失效;如果 or 條件的列都加了索引,索引可能會(huì)走的,大家可以自己試一試。

          like通配符可能導(dǎo)致索引失效

          在業(yè)務(wù)中我們喜歡用模糊搜索的方式去做查詢,舉個(gè)例子,我們想搜索名稱為 leixiaoshuai 小伙子:

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?name?=?'%leixiaoshuai%';

          查看執(zhí)行計(jì)劃的結(jié)果,發(fā)現(xiàn)是全表掃描,說明索引失效了:


          我們?cè)囍亚懊娴?去掉,再查一遍:

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?name?=?'leixiaoshuai%';

          執(zhí)行計(jì)劃告訴我們正常走索引了:


          我們?cè)侔?加回來,這次不查詢所有的字段,只查詢 id 和 name:

          EXPLAIN
          SELECT?id,?name?FROM?t_user?WHERE?name?=?'%leixiaoshuai%';

          你會(huì)驚奇的發(fā)現(xiàn)居然又走索引了:


          驚不驚喜意不意外,這是因?yàn)楦采w索引的原因。

          分析結(jié)論:

          like查詢以%開頭,會(huì)導(dǎo)致索引失效。可以有兩種方式優(yōu)化:

          • 使用覆蓋索引
          • 把%放后面

          敲黑板 索引包含所有滿足查詢需要的數(shù)據(jù)的索引,稱為覆蓋索引(Covering Index)。

          字符串列使用 where 一定用引號(hào)括起來,否則索引失效

          從上面的建表語句中我們知道 name 列是 varchar 可變字符類型,如果我們?cè)诓樵儠r(shí)忘了加引號(hào)會(huì)發(fā)生呢?

          我們做一個(gè)簡(jiǎn)單的查詢,此時(shí) name 列沒有加引號(hào):

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?name?=?123;

          name 列是建了普通索引的,理論上是應(yīng)該走索引的,實(shí)際上是全表掃描:


          驚不驚喜意不意外,嚇得我趕緊把引號(hào)加起來了:

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?name?=?'123';

          這次果然乖乖走索引了:


          分析結(jié)論:如果某一列是字符類型,但是使用 where 不加單引號(hào)時(shí),是字符串跟數(shù)字的比較,它們類型不匹配,MySQL會(huì)做隱式的類型轉(zhuǎn)換,把它們轉(zhuǎn)換為浮點(diǎn)數(shù)再做比較。

          在索引列上使用mysql的內(nèi)置函數(shù),索引失效

          name 列已經(jīng)加了普通索引,但是在查詢的時(shí)候給加了內(nèi)置函數(shù),作用是將 name 列變成全大寫:

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?UPPER(name)?=?'LEIXIAOSHUAI';

          索引直接 GG 了,套了一層殼子,我就不認(rèn)識(shí)你了,我直接全表掃描:


          索引字段上使用is null或is not null,可能導(dǎo)致索引失效

          根據(jù)前面的建表語句我們知道 name 列,address 列都加了普通索引。

          (1)查找 name 不為空的所有列

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?name?is?not?null;

          意料之中,正常走了索引:


          (2)查找 address 不為空的所有列

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?address?is?not?null;

          意料之中,正常走了索引:


          (3)查找 name 不為空或者 address 不為空

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?name?is?not?null?or?address?is?not?null;

          咦???索引好像失效了:


          對(duì)索引列運(yùn)算索引失效

          對(duì)索引列進(jìn)行四則運(yùn)算(如,+、-、*、/),索引會(huì)失效。

          user_id 是索引列,在查詢的時(shí)候進(jìn)行+1

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?user_id+1=?456789;

          索引直接迷路了:


          聯(lián)合索引ABC問題導(dǎo)致索引失效

          我們重新建一張表,id 是主鍵,name 和 age 列加了一個(gè)聯(lián)合索引。

          CREATE?TABLE?`t_user`?(
          ??`id`?int?NOT?NULL?AUTO_INCREMENT,
          ??`name`?varchar(255)?DEFAULT?NULL,
          ??`age`?int?DEFAULT?NULL,
          ??`address`?varchar(255)?DEFAULT?NULL,
          ??`user_id`?int?DEFAULT?NULL,
          ??PRIMARY?KEY?(`id`),
          ??KEY?`index_name_age`?(`name`,`age`)?USING?BTREE
          )?ENGINE=InnoDB;

          特別要注意一下聯(lián)合索引的順序,name 在前,age 在后。

          (1)where 條件同時(shí)滿足 name 和 age

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?name?=?'leixiaoshuai'?AND?age?=?18;

          毫無疑問肯定是走了索引:


          (2)where 條件只有 name 列,這個(gè)能否走索引呢?

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?name?=?'leixiaoshuai';

          看結(jié)果,依然是走了聯(lián)合索引,開心~


          (3)where 條件只有 age 列,這個(gè)應(yīng)該也可以走索引吧?!

          EXPLAIN
          SELECT?*?FROM?t_user?WHERE?age?=?18;

          完蛋,翻車了,沒走索引……


          分析結(jié)論:

          • 當(dāng)我們創(chuàng)建一個(gè)聯(lián)合索引的時(shí)候,如(k1,k2,k3),相當(dāng)于創(chuàng)建了(k1)、(k1,k2)和(k1,k2,k3)三個(gè)索引,這就是最左匹配原則。
          • 聯(lián)合索引不滿足最左原則,索引一般會(huì)失效,但是這個(gè)還跟Mysql優(yōu)化器有關(guān)的。

          總結(jié):

          以上就是使用索引時(shí)最常見的 7 種翻車場(chǎng)景,大家有沒有中過招?

          最后,我向領(lǐng)導(dǎo)也坦白了,我是因?yàn)槁?lián)合索引 ABC 問題中招的,實(shí)在是草率了……

          -- End --

          這些文章你可得看:

          ????『死磕Java并發(fā)編程系列』 01 十張圖告訴你多線程那些破事

          『死磕Java并發(fā)編程系列』 02 面試官:說說什么是Java內(nèi)存模型?

          『死磕Java并發(fā)編程系列』 03 面試必問的CAS原理你會(huì)了嗎?

          『死磕Java并發(fā)編程系列』 04 面試官:說說Atomic原子類的實(shí)現(xiàn)原理?

          ????『死磕Java并發(fā)編程系列』 05 圖解Java中那18 把鎖.md

          作者簡(jiǎn)介:

          博主從華中科技大學(xué)碩士畢業(yè),是一個(gè)對(duì)技術(shù)有追求,對(duì)生活有激情的程序員。

          幾年間浪跡于多個(gè)一線互聯(lián)網(wǎng)大廠,具有多年開發(fā)實(shí)戰(zhàn)經(jīng)驗(yàn)。

          如果你熱愛技術(shù)或者你也不滿足現(xiàn)狀喜歡搞事情,那你不妨關(guān)注我,讓我們一路同行,一起分享技術(shù)干貨、交流面試技巧,吐槽職場(chǎng)故事。

          我有技術(shù)和故事,你來嗎?

          我該不會(huì)被辭退了吧

          瀏覽 45
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  欧美风情在线 | 久久夜色国产精品 | 神马午夜国产精品 | 手机黄色在线 | 四虎影院日韩无码 |