<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>

          老司機(jī)總結(jié)的12條 SQL 優(yōu)化方案(建議收藏)

          共 10050字,需瀏覽 21分鐘

           ·

          2022-07-09 20:12

          不點(diǎn)藍(lán)字關(guān)注,我們哪來(lái)故事?



          在開(kāi)始介紹如何優(yōu)化sql前,先附上mysql內(nèi)部邏輯圖讓大家有所了解

          (1)連接器: 主要負(fù)責(zé)跟客戶端建立連接、獲取權(quán)限、維持和管理連接

          (2)查詢緩存: 優(yōu)先在緩存中進(jìn)行查詢,如果查到了則直接返回,如果緩存中查詢不到,在去數(shù)據(jù)庫(kù)中查詢。

          MySQL緩存是默認(rèn)關(guān)閉的,也就是說(shuō)不推薦使用緩存,并且在MySQL8.0 版本已經(jīng)將查詢緩存的整塊功能刪掉了。這主要是它的使用場(chǎng)景限制造成的:

          • 先說(shuō)下緩存中數(shù)據(jù)存儲(chǔ)格式:key(sql語(yǔ)句)- value(數(shù)據(jù)值),所以如果SQL語(yǔ)句(key)只要存在一點(diǎn)不同之處就會(huì)直接進(jìn)行數(shù)據(jù)庫(kù)查詢了;
          • 由于表中的數(shù)據(jù)不是一成不變的,大多數(shù)是經(jīng)常變化的,而當(dāng)數(shù)據(jù)庫(kù)中的數(shù)據(jù)變化了,那么相應(yīng)的與此表相關(guān)的緩存數(shù)據(jù)就需要移除掉;

          (3)解析器/分析器: 分析器的工作主要是對(duì)要執(zhí)行的SQL語(yǔ)句進(jìn)行詞法解析、語(yǔ)法解析,最終得到抽象語(yǔ)法樹,然后再使用預(yù)處理器對(duì)抽象語(yǔ)法樹進(jìn)行語(yǔ)義校驗(yàn),判斷抽象語(yǔ)法樹中的表是否存在,如果存在的話,在接著判斷select投影列字段是否在表中存在等。

          (4)優(yōu)化器: 主要將SQL經(jīng)過(guò)詞法解析、語(yǔ)法解析后得到的語(yǔ)法樹,通過(guò)數(shù)據(jù)字典和統(tǒng)計(jì)信息的內(nèi)容,再經(jīng)過(guò)一系列運(yùn)算 ,最終得出一個(gè)執(zhí)行計(jì)劃,包括選擇使用哪個(gè)索引

          • 在分析是否走索引查詢時(shí),是通過(guò)進(jìn)行動(dòng)態(tài)數(shù)據(jù)采樣統(tǒng)計(jì)分析出來(lái);只要是統(tǒng)計(jì)分析出來(lái)的,那就可能會(huì)存在分析錯(cuò)誤的情況,所以在SQL執(zhí)行不走索引時(shí),也要考慮到這方面的因素

          (5)執(zhí)行器: 根據(jù)一系列的執(zhí)行計(jì)劃去調(diào)用存儲(chǔ)引擎提供的API接口去調(diào)用操作數(shù)據(jù),完成SQL的執(zhí)行。

          一、SQL語(yǔ)句及索引的優(yōu)化

          SQL語(yǔ)句的優(yōu)化

          1. 盡量避免使用子查詢

          例:

          SELECT * FROM t1 WHERE id (SELECT id FROM t2 WHERE name = 'chackca');

          其子查詢?cè)贛ysql5.5版本里,內(nèi)部執(zhí)行計(jì)劃是這樣:先查外表再匹配內(nèi)表,而不是先查內(nèi)表t2,當(dāng)外表的數(shù)據(jù)很大時(shí),查詢速度會(huì)非常慢。

          在MariaDB10/Mysql5.6版本里,采用join關(guān)聯(lián)方式對(duì)其進(jìn)行了優(yōu)化,這條SQL語(yǔ)句會(huì)自動(dòng)轉(zhuǎn)換為:SELECT t1.* FROM t1 JOIN t2 on t1.id = t2.id

          但請(qǐng)注意的是:優(yōu)化只針對(duì)SELECT有效,對(duì)UPDATE/DELETE子查詢無(wú)效,固生產(chǎn)環(huán)境應(yīng)避免使用子查詢

          由于MySQL的優(yōu)化器對(duì)于子查詢的處理能力比較弱,所以不建議使用子查詢,可以改寫成Inner Join,之所以 join 連接效率更高,是因?yàn)?MySQL不需要在內(nèi)存中創(chuàng)建臨時(shí)表

          2. 用IN來(lái)替換OR

          • 低效查詢:SELECT * FROM t WHERE id = 10 OR id = 20 OR id = 30;
          • 高效查詢:SELECT * FROM t WHERE id IN (10,20,30);

          另外,MySQL對(duì)于IN做了相應(yīng)的優(yōu)化,即將IN中的常量全部存儲(chǔ)在一個(gè)數(shù)組里面,而且這個(gè)數(shù)組是排好序的。但是如果數(shù)值較多,產(chǎn)生的消耗也是比較大的。再例如:select id from table_name where num in(1,2,3) 對(duì)于連續(xù)的數(shù)值,能用 between 就不要用 in 了;再或者使用連接來(lái)替換。

          3. 讀取適當(dāng)?shù)挠涗汱IMIT M,N,而不要讀多余的記錄

          select id,name from t limit 86661320

          使用上述sql語(yǔ)句做分頁(yè)的時(shí)候,可能有人會(huì)發(fā)現(xiàn),隨著表數(shù)據(jù)量的增加,直接使用limit分頁(yè)查詢會(huì)越來(lái)越慢。

          對(duì)于 limit m, n 的分頁(yè)查詢,越往后面翻頁(yè)(即m越大的情況下)SQL的耗時(shí)會(huì)越來(lái)越長(zhǎng),對(duì)于這種應(yīng)該先取出主鍵id,然后通過(guò)主鍵id跟原表進(jìn)行Join關(guān)聯(lián)查詢。因?yàn)镸ySQL 并不是跳過(guò) offset 行,而是取 offset+N 行,然后放棄前 offset 行,返回 N 行,那當(dāng) offset 特別大的時(shí)候,效率就非常的低下,要么控制返回的總頁(yè)數(shù),要么對(duì)超過(guò)特定閾值的頁(yè)數(shù)進(jìn)行 SQL 改寫。

          優(yōu)化的方法如下:可以取前一頁(yè)的最大行數(shù)的id(將上次遍歷到的最末尾的數(shù)據(jù)ID傳給數(shù)據(jù)庫(kù),然后直接定位到該ID處,再往后面遍歷數(shù)據(jù)),然后根據(jù)這個(gè)最大的id來(lái)限制下一頁(yè)的起點(diǎn)。比如此列中,上一頁(yè)最大的id是866612。sql可以采用如下的寫法:

          select id,name from table_name where id866612 limit 20

          4. 禁止不必要的Order By排序

          如果我們對(duì)結(jié)果沒(méi)有排序的要求,就盡量少用排序;

          如果排序字段沒(méi)有用到索引,也盡量少用排序;

          另外,分組統(tǒng)計(jì)查詢時(shí)可以禁止其默認(rèn)排序

          SELECT goods_id,count(*) FROM t GROUP BY goods_id;

          默認(rèn)情況下,Mysql會(huì)對(duì)所有的GROUP BT col1,col2…的字段進(jìn)行排序,也就是說(shuō)上述會(huì)對(duì) goods_id進(jìn)行排序,如果想要避免排序結(jié)果的消耗,可以指定ORDER BY NULL禁止排序:

          SELECT goods_id,count(*) FROM t GROUP BY goods_id ORDER BY NULL

          5. 總和查詢可以禁止排重用union all

          union和union all的差異主要是前者需要將結(jié)果集合并后再進(jìn)行唯一性過(guò)濾操作,這就會(huì)涉及到排序,增加大量的CPU運(yùn)算,加大資源消耗及延遲。

          當(dāng)然,union all的前提條件是兩個(gè)結(jié)果集沒(méi)有重復(fù)數(shù)據(jù)。所以一般是我們明確知道不會(huì)出現(xiàn)重復(fù)數(shù)據(jù)的時(shí)候才建議使用 union all 提高速度。

          6. 避免隨機(jī)取記錄

          SELECT * FROM t1 WHERE 1 = 1 ORDER BY RAND() LIMIT 4;
                  SELECT * FROM t1 WHERE id >= CEIL(RAND()*1000LIMIT 4;

          以上兩個(gè)語(yǔ)句都無(wú)法用到索引

          7. 將多次插入換成批量Insert插入

          INSERT INTO t(idnameVALUES(1'aaa');
                  INSERT INTO t(idnameVALUES(2'bbb');
                  INSERT INTO t(idnameVALUES(3'ccc');
                  —>
                  INSERT INTO t(idnameVALUES(1'aaa'),(2'bbb'),(3'ccc');

          8. 只返回必要的列,用具體的字段列表代替 select * 語(yǔ)句

          SELECT * 會(huì)增加很多不必要的消耗(cpu、io、內(nèi)存、網(wǎng)絡(luò)帶寬);增加了使用覆蓋索引的可能性;當(dāng)表結(jié)構(gòu)發(fā)生改變時(shí),前者也需要經(jīng)常更新。所以要求直接在select后面接上字段名。

          MySQL數(shù)據(jù)庫(kù)是按照行的方式存儲(chǔ),而數(shù)據(jù)存取操作都是以一個(gè)頁(yè)大小進(jìn)行IO操作的,每個(gè)IO單元中存儲(chǔ)了多行,每行都是存儲(chǔ)了該行的所有字段。所以無(wú)論取一個(gè)字段還是多個(gè)字段,實(shí)際上數(shù)據(jù)庫(kù)在表中需要訪問(wèn)的數(shù)據(jù)量其實(shí)是一樣的。

          但是如果查詢的字段都在索引中,也就是覆蓋索引,那么可以直接從索引中獲取對(duì)應(yīng)的內(nèi)容直接返回,不需要進(jìn)行回表,減少IO操作。除此之外,當(dāng)存在 order by 操作的時(shí)候,select 子句中的字段多少會(huì)在很大程度上影響到我們的排序效率。

          9. 區(qū)分in和exists

          select * from 表A where id in (select id from 表B)

          上面的語(yǔ)句相當(dāng)于:

          select * from 表A where exists(select * from 表B where 表B.id=表A.id)

          區(qū)分in和exists主要是造成了驅(qū)動(dòng)順序的改變(這是性能變化的關(guān)鍵),如果是exists,那么以外層表為驅(qū)動(dòng)表,先被訪問(wèn),如果是IN,那么先執(zhí)行子查詢。所以IN適合于外表大而內(nèi)表小的情況;EXISTS適合于外表小而內(nèi)表大的情況。

          另外,in查詢?cè)谀承┣闆r下有可能會(huì)查詢返回錯(cuò)誤的結(jié)果,因此,通常是建議在確定且有限的集合時(shí),可以使用in。如 IN (0,1,2)。

          10. 優(yōu)化Group By語(yǔ)句

          如果對(duì)group by語(yǔ)句的結(jié)果沒(méi)有排序要求,要在語(yǔ)句后面加 order by null(group 默認(rèn)會(huì)排序);

          盡量讓group by過(guò)程用上表的索引,確認(rèn)方法是explain結(jié)果里沒(méi)有Using temporaryUsing filesort

          如果group by需要統(tǒng)計(jì)的數(shù)據(jù)量不大,盡量只使用內(nèi)存臨時(shí)表;也可以通過(guò)適當(dāng)調(diào)大tmp_table_size參數(shù),來(lái)避免用到磁盤臨時(shí)表;

          • 如果數(shù)據(jù)量實(shí)在太大,使用SQL_BIG_RESULT這個(gè)提示,來(lái)告訴優(yōu)化器直接使用排序算法(直接用磁盤臨時(shí)表)得到group by的結(jié)果。

          使用where子句替換Having子句:避免使用having子句,having只會(huì)在檢索出所有記錄之后才會(huì)對(duì)結(jié)果集進(jìn)行過(guò)濾,這個(gè)處理需要排序分組,如果能通過(guò)where子句提前過(guò)濾查詢的數(shù)目,就可以減少這方面的開(kāi)銷。

          • 低效: SELECT JOB, AVG(SAL) FROM EMP GROUP by JOB HAVING JOB = ‘PRESIDENT’ OR JOB = ‘MANAGER’
          • 高效: SELECT JOB, AVG(SAL) FROM EMP WHERE JOB = ‘PRESIDENT’ OR JOB = ‘MANAGER’ GROUP by JOB

          11. 盡量使用數(shù)字型字段

          若只含數(shù)值信息的字段盡量不要設(shè)計(jì)為字符型,這會(huì)降低查詢和連接的性能。引擎在處理查詢和連接時(shí)會(huì)逐個(gè)比較字符串中每一個(gè)字符,而對(duì)于數(shù)字型而言只需要比較一次就夠了。

          12. 優(yōu)化Join語(yǔ)句

          當(dāng)我們執(zhí)行兩個(gè)表的Join的時(shí)候,就會(huì)有一個(gè)比較的過(guò)程,逐條比較兩個(gè)表的語(yǔ)句是比較慢的,因此可以把兩個(gè)表中數(shù)據(jù)依次讀進(jìn)一個(gè)內(nèi)存塊中,在Mysql中執(zhí)行:show variables like ‘join_buffer_size’,可以看到j(luò)oin在內(nèi)存中的緩存池大小,其大小將會(huì)影響join語(yǔ)句的性能。在執(zhí)行join的時(shí)候,數(shù)據(jù)庫(kù)會(huì)選擇一個(gè)表把他要返回以及需要進(jìn)行和其他表進(jìn)行比較的數(shù)據(jù)放進(jìn)join_buffer

          什么是驅(qū)動(dòng)表,什么是被驅(qū)動(dòng)表,這兩個(gè)概念在查詢中有時(shí)容易讓人搞混,有下面幾種情況,大家需要了解。

          1.當(dāng)連接查詢沒(méi)有where條件時(shí)

          • left join 前面的表是驅(qū)動(dòng)表,后面的表是被驅(qū)動(dòng)表
          • right join 后面的表是驅(qū)動(dòng)表,前面的表是被驅(qū)動(dòng)表
          • inner join / join 會(huì)自動(dòng)選擇表數(shù)據(jù)比較少的作為驅(qū)動(dòng)表
          • straight_join(≈join) 直接選擇左邊的表作為驅(qū)動(dòng)表(語(yǔ)義上與join類似,但去除了join自動(dòng)選擇小表作為驅(qū)動(dòng)表的特性)

          2.當(dāng)連接查詢有where條件時(shí),帶where條件的表是驅(qū)動(dòng)表,否則是被驅(qū)動(dòng)表

          假設(shè)有表如右邊:t1與t2表完全一樣,a字段有索引,b無(wú)索引,t1有100條數(shù)據(jù),t2有1000條數(shù)據(jù)

          若被驅(qū)動(dòng)表有索引,那么其執(zhí)行算法為:Index Nested-Loop Join(NLJ),示例如下:

          1.執(zhí)行語(yǔ)句:select * from t1 straight_join t2 on (t1.a=t2.a);由于被驅(qū)動(dòng)表t2.a是有索引的,其執(zhí)行邏輯如下:

          • 從表t1中讀入一行數(shù)據(jù) R;
          • 從數(shù)據(jù)行R中,取出a字段到表t2里去查找;
          • 取出表t2中滿足條件的行,跟R組成一行,作為結(jié)果集的一部分;
          • 重復(fù)執(zhí)行步驟1到3,直到表t1的末尾循環(huán)結(jié)束。
          • 如果一條join語(yǔ)句的Extra字段什么都沒(méi)寫的話,就表示使用的是NLJ算法

          若被驅(qū)動(dòng)表無(wú)索引,那么其執(zhí)行算法為:Block Nested-Loop Join(BLJ)(Block 塊,每次都會(huì)取一塊數(shù)據(jù)到內(nèi)存以減少I/O的開(kāi)銷),示例如下:

          2.執(zhí)行語(yǔ)句:select * from t1 straight_join t2 on (t1.a=t2.b);由于被驅(qū)動(dòng)表t2.b是沒(méi)有索引的,其執(zhí)行邏輯如下:

          • 把驅(qū)動(dòng)表t1的數(shù)據(jù)讀入線程內(nèi)存join_buffer(無(wú)序數(shù)組)中,由于我們這個(gè)語(yǔ)句中寫的是select *,因此是把整個(gè)表t1放入了內(nèi)存;
          • 順序遍歷表t2,把表t2中的每一行取出來(lái),跟join_buffer中的數(shù)據(jù)做對(duì)比,滿足join條件的,作為結(jié)果集的一部分返回。

          3.另外還有一種算法為Simple Nested-Loop Join(SLJ),其邏輯為:順序取出驅(qū)動(dòng)表中的每一行數(shù)據(jù),到被驅(qū)動(dòng)表去做全表掃描匹配,匹配成功則作為結(jié)果集的一部分返回。

          另外,Innodb會(huì)為每個(gè)數(shù)據(jù)表分配一個(gè)存儲(chǔ)在磁盤的 表名.ibd 文件,若關(guān)聯(lián)的表過(guò)多,將會(huì)導(dǎo)致查詢的時(shí)候磁盤的磁頭移動(dòng)次數(shù)過(guò)多,從而影響性能

          所以實(shí)踐中,盡可能減少Join語(yǔ)句中的NestedLoop的循環(huán)次數(shù):“永遠(yuǎn)用小結(jié)果集驅(qū)動(dòng)大的結(jié)果集”

          • 用小結(jié)果集驅(qū)動(dòng)大結(jié)果集,將篩選結(jié)果小的表(在決定哪個(gè)表做驅(qū)動(dòng)表的時(shí)候,應(yīng)該是兩個(gè)表按照各自的條件過(guò)濾,過(guò)濾完成之后,計(jì)算參與join的各個(gè)字段的總數(shù)據(jù)量,數(shù)據(jù)量小的那個(gè)表,就是“小表”)首先連接,再去連接結(jié)果集比較大的表,盡量減少join語(yǔ)句中的Nested Loop的循環(huán)總次數(shù)

          • 優(yōu)先優(yōu)化Nested Loop的內(nèi)層循環(huán)(也就是最外層的Join連接),因?yàn)閮?nèi)層循環(huán)是循環(huán)中執(zhí)行次數(shù)最多的,每次循環(huán)提升很小的性能都能在整個(gè)循環(huán)中提升很大的性能;

          • 對(duì)被驅(qū)動(dòng)表的join字段上建立索引;

          • 當(dāng)被驅(qū)動(dòng)表的join字段上無(wú)法建立索引的時(shí)候,設(shè)置足夠的Join Buffer Size。

          • 盡量用inner join(因?yàn)槠鋾?huì)自動(dòng)選擇小表去驅(qū)動(dòng)大表).避免 LEFT JOIN (一般我們使用Left Join的場(chǎng)景是大表驅(qū)動(dòng)小表)和NULL,那么如何優(yōu)化Left Join呢?


            • 條件中盡量能夠過(guò)濾一些行將驅(qū)動(dòng)表變得小一點(diǎn),用小表去驅(qū)動(dòng)大表
          • 右表的條件列一定要加上索引(主鍵、唯一索引、前綴索引等),最好能夠使type達(dá)到range及以上(ref,eq_ref,const,system)

          • 適當(dāng)?shù)卦诒砝锩嫣砑尤哂嘈畔?lái)減少join的次數(shù)

          • 使用更快的固態(tài)硬盤

          性能優(yōu)化,left join 是由左邊決定的,左邊一定都有,所以右邊是我們的關(guān)鍵點(diǎn),建立索引要建在右邊。當(dāng)然如果索引是在左邊的,我們可以考慮使用右連接,如下

          select * from atable left join btable on atable.aid=btable.bid;
                  -- 最好在bid上建索引

          Tips:Join左連接在右邊建立索引;組合索引則盡量將數(shù)據(jù)量大的放在左邊,在左邊建立索引

          索引的優(yōu)化/如何避免索引失效

          1.最佳左前綴法則

          如果索引了多列,要遵守最左前綴法則,指的是查詢從索引的最左前列開(kāi)始并且不跳過(guò)索引中的列。Mysql查詢優(yōu)化器會(huì)對(duì)查詢的字段進(jìn)行改進(jìn),判斷查詢的字段以哪種形式組合能使得查詢更快,所有比如創(chuàng)建的是(a,b)索引,查詢的是(b,a),查詢優(yōu)化器會(huì)修改成(a,b)后使用索引查詢。

          2.不在索引列上做任何操作

          1.計(jì)算:對(duì)索引進(jìn)行表達(dá)式計(jì)算會(huì)導(dǎo)致索引失效,如 where id + 1 = 10,可以轉(zhuǎn)換成 where id = 10 -1,這樣就可以走索引

          2.函數(shù):select * from t_user where length(name)=6; 此語(yǔ)句對(duì)字段使用到了函數(shù),會(huì)導(dǎo)致索引失效

          從 MySQL 8.0 開(kāi)始,索引特性增加了函數(shù)索引,即可以針對(duì)函數(shù)計(jì)算后的值建立一個(gè)索引,也就是說(shuō)該索引的值是函數(shù)計(jì)算后的值,所以就可以通過(guò)掃描索引來(lái)查詢數(shù)據(jù)。

          alter table t_user add key idx_name_length ((length(name)));

          (自動(dòng)/手動(dòng))類型轉(zhuǎn)換

          • (字符串類型必須帶''引號(hào)才能使索引生效)字段是varchar,用整型進(jìn)行查詢時(shí),無(wú)法走索引,如select * from user where phone = 13030303030

          Mysql 在執(zhí)行上述語(yǔ)句時(shí),會(huì)把字段轉(zhuǎn)換為數(shù)字再進(jìn)行比較,所以上面那條語(yǔ)句就相當(dāng)于:select * from user where CAST(phone AS signed int) = 13030303030; CAST 函數(shù)是作用在了 phone 字段,而 phone 字段是索引,也就是對(duì)索引使用了函數(shù)!所以索引失效

          • 字段是int,用string進(jìn)行查詢時(shí),mysql會(huì)自動(dòng)轉(zhuǎn)化,可以走索引,如:select * from user where id = '1'

          MySQL 在遇到字符串和數(shù)字比較的時(shí)候,會(huì)自動(dòng)把字符串轉(zhuǎn)為數(shù)字,然后再進(jìn)行比較。以上這條語(yǔ)句相當(dāng)于:select * from user where id = CAST(“1” AS signed int),索引字段并沒(méi)有用任何函數(shù),CAST 函數(shù)是用在了輸入?yún)?shù),因此是可以走索引掃描的。

          3.存儲(chǔ)引擎不能使用索引中范圍條件右邊的列。

          如這樣的sql: select * from user where username='123' and age>20 and phone='1390012345',其中username, age, phone都有索引,只有username和age會(huì)生效,phone的索引沒(méi)有用到。

          4.盡量使用覆蓋索引(只訪問(wèn)索引的查詢(索引列和查詢列一致))

          select age from user,減少select *

          5.mysql在使用負(fù)向查詢條件(!=、<>、not in、not exists、not like)的時(shí)候無(wú)法使用索引會(huì)導(dǎo)致全表掃描。

          你可以想象一下,對(duì)于一棵B+樹,根節(jié)點(diǎn)是40,如果你的條件是等于20,就去左面查,你的條件等于50,就去右面查,但是你的條件是不等于66,索引應(yīng)該咋辦?還不是遍歷一遍才知道。

          6.is null, is not null 也無(wú)法使用索引,在實(shí)際中盡量不要使用null(避免在 where 子句中對(duì)字段進(jìn)行 null 值判斷) 不過(guò)在mysql的高版本已經(jīng)做了優(yōu)化,允許使用索引

          對(duì)于null的判斷會(huì)導(dǎo)致引擎放棄使用索引而進(jìn)行全表掃描。

          7.like 以通配符開(kāi)頭(%abc..)時(shí),mysql索引失效會(huì)變成全表掃描的操作。

          所以最好用右邊like ‘a(chǎn)bc%’。如果兩邊都要用,可以用select username from user where username like '%abc%',其中username是必須是索引列,才可讓索引生效

          假如index(a,b,c), where a=3 and b like ‘a(chǎn)bc%’ and c=4,a能用,b能用,c不能用,類似于不能使用范圍條件右邊的列的索引

          對(duì)于一棵B+樹索引來(lái)講,如果根節(jié)點(diǎn)是字符def,假如查詢條件的通配符在后面,例如abc%,則其知道應(yīng)該搜索左子樹,假如傳入為efg%,則應(yīng)該搜索右子樹,如果通配符在前面%abc,則數(shù)據(jù)庫(kù)不知道應(yīng)該走哪一面,就都掃描一遍了。

          8.少用or,在 WHERE 子句中,如果在 OR 前的條件列是索引列,而在 OR 后的條件列不是索引列,那么索引會(huì)失效。

          select * from t_user where id = 1 or age = 18;
                  -- id有索引,name沒(méi)有,此時(shí)沒(méi)法走索引

          因?yàn)?OR 的含義就是兩個(gè)只要滿足一個(gè)即可,因此只有一個(gè)條件列是索引列是沒(méi)有意義的,只要有條件列不是索引列,就會(huì)進(jìn)行全表掃描。

          必須要or前后的字段都有索引,查詢才能使用上索引(分別使用,最后合并結(jié)果type = index_merge

          9.在組合/聯(lián)合索引中,將有區(qū)分度的索引放在前面

          如果沒(méi)有區(qū)分度,例如用性別,相當(dāng)于把整個(gè)大表分成兩部分,查找數(shù)據(jù)還是需要遍歷半個(gè)表才能找到,使得索引失去了意義。

          10.使用前綴索引

          短索引不僅可以提高查詢性能而且可以節(jié)省磁盤空間和I/O操作,減少索引文件的維護(hù)開(kāi)銷,但缺點(diǎn)是不能用于 ORDER BY 和 GROUP BY 操作,也不能用于覆蓋索引。

          比如有一個(gè)varchar(255)的列,如果該列在前10個(gè)或20個(gè)字符內(nèi),可以做到既使前綴索引的區(qū)分度接近全列索引,那么就不要對(duì)整個(gè)列進(jìn)行索引。為了減少key_len,可以考慮創(chuàng)建前綴索引,即指定一個(gè)前綴長(zhǎng)度,可以使用count(distinct leftIndex(列名, 索引長(zhǎng)度))/count(*) 來(lái)計(jì)算前綴索引的區(qū)分度。

          11.SQL 性能優(yōu)化 explain 中的 type:至少要達(dá)到 range 級(jí)別,要求是 ref 級(jí)別,如果可以是 consts 最好。

          • consts:?jiǎn)伪碇凶疃嘀挥幸粋€(gè)匹配行(主鍵或者唯一索引),在優(yōu)化階段即可讀取到數(shù)據(jù)。
          • ref:使用普通的索引
          • range:對(duì)索引進(jìn)行范圍檢索。

          當(dāng) type=index 時(shí),索引物理文件全掃,速度非常慢。

          基于 Spring Boot + MyBatis Plus + Vue & Element 實(shí)現(xiàn)的后臺(tái)管理系統(tǒng) + 用戶小程序,支持 RBAC 動(dòng)態(tài)權(quán)限、多租戶、數(shù)據(jù)權(quán)限、工作流、三方登錄、支付、短信、商城等功能。

          項(xiàng)目地址:https://github.com/YunaiV/ruoyi-vue-pro

          二、數(shù)據(jù)庫(kù)表結(jié)構(gòu)的優(yōu)化:使得數(shù)據(jù)庫(kù)結(jié)構(gòu)符合三大范式與BCNF

          https://blog.csdn.net/qq_35642036/article/details/82809974

          基于微服務(wù)的思想,構(gòu)建在 B2C 電商場(chǎng)景下的項(xiàng)目實(shí)戰(zhàn)。核心技術(shù)棧,是 Spring Boot + Dubbo 。未來(lái),會(huì)重構(gòu)成 Spring Cloud Alibaba 。

          項(xiàng)目地址:https://github.com/YunaiV/onemall

          三、系統(tǒng)配置的優(yōu)化

          四、硬件的優(yōu)化


          END



          若覺(jué)得文章對(duì)你有幫助,隨手轉(zhuǎn)發(fā)分享,也是我們繼續(xù)更新的動(dòng)力。


          長(zhǎng)按二維碼,掃掃關(guān)注哦

          ?「C語(yǔ)言中文網(wǎng)」官方公眾號(hào),關(guān)注手機(jī)閱讀教程 ?

          瀏覽 48
          點(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>
                  美国三级欧美一级 | 大香蕉一人在线网 | 色姑娘超碰狠狠操五月 | 日韩欧美三级片www麻豆 | 成人蘑菇视频 |