MYSQL索引及查詢優化

一、什麼是索引

        索引類似是大型圖書館建立書目索引,可以提高數據檢索的效率,降低數據庫的IO成本。MySQL在數據量較大(官方文檔說500~800w記錄)的情況下性能開始逐漸下降,所以大數據量建立索引來提高數據的檢索效率是非常有必要的。

1.1、索引的概念

       MySQL官方對索引的定義爲:索引(Index)是幫助MySQL高效獲取數據的數據結構。我們可以簡單理解爲:快速查找排好序的一種數據結構。

       索引是對數據庫表中一列或多列的值進行排序的一種結構。在關係數據庫中,索引是一種與表有關的數據庫結構,它可以使對應於表的SQL語句執行得更快。索引的作用相當於圖書的目錄,可以根據目錄中的頁碼快速找到所需的內容。當表中有大量記錄時,若要對錶進行查詢,第一種搜索信息方式是全表搜索,是將所有記錄一一取出,和查詢條件進行一一對比,然後返回滿足條件的記錄,這樣做會消耗大量數據庫系統時間,並造成大量磁盤I/O操作;第二種就是在表中建立索引,然後在索引中找到符合查詢條件的索引值,最後通過保存在索引中的ROWID(相當於頁碼)快速找到表中對應的記錄。

       索引是一個單獨的、物理的數據庫結構,它是某個表中一列或若干列值的集合和相應的指向表中物理標識這些值的數據頁的邏輯指針清單。索引提供指向存儲在表的指定列中的數據值的指針,然後根據您指定的排序順序對這些指針排序。數據庫使用索引的方式與您使用書籍中的索引的方式很相似:它搜索索引以找到特定值,然後順指針找到包含該值的行。在數據庫關係圖中,可以在選定表的“索引/鍵”屬性頁中創建、編輯或刪除每個索引類型。當保存索引所附加到的表,或保存該表所在的關係圖時,索引將保存在數據庫中。

       MYSQL索引主要有兩種結構:B+Tree索引和Hash索引。我們平常所說的索引,如果沒有特別指明,一般都是指B樹結構組織的索引(B+Tree索引)。B+Tree索引結構圖如下所示:

說明:

      最外層淺藍色磁盤塊1裏有數據17、35(深藍色)和指針P1、P2、P3(黃色)。P1指針表示小於17的磁盤塊,P2是在17-35之間,P3指向大於35的磁盤塊。真實數據存在於子葉節點也就是最底下的一層3、5、9、10、13……等,非葉子節點不存儲真實的數據,只存儲指引搜索方向的數據項,如17、35。

     查找過程:例如搜索28數據項,首先加載磁盤塊1到內存中,發生一次I/O,用二分查找確定在P2指針。接着發現28在26和30之間,通過P2指針的地址加載磁盤塊3到內存,發生第二次I/O。用同樣的方式找到磁盤塊8,發生第三次I/O。

     真實的情況是,上面3層的B+Tree可以表示上百萬的數據,在上百萬的數據只發生了三次I/O而不是上百萬次I/O,時間提升是巨大的。

1.2、索引的創建

1)首先是要肯定是根據業務經常查詢的語句;

2)儘量選擇區分度高的列作爲索引,區分度的公式是 COUNT(DISTINCT col) / COUNT(*)。表示字段不重複的比率,比率越大我們掃描的記錄數就越少;

3)如果業務中唯一特性最好建立唯一鍵,一方面可以保證數據的正確性,另一方面索引的效率能大大提高。

4)創建索引腳本

ALTER TABLE sys_connect ADD INDEX `idx_createTime`(`create_time`) USING BTREE

1.3、導致SQL執行慢的原因

1)硬件問題。如網絡速度慢,內存不足,I/O吞吐量小,磁盤空間滿了等;

2)服務器調優及各個參數設置(調整my.cnf;

3)數據過多(分庫分表);

4) 沒有索引或者索引失效。

1.4、索引分析Explain

MySQL提供了Explain,用於顯示SQL執行的詳細信息,可以進行索引的優化。

以查詢店長級別爲1的SQL語句分析索引:

例:

EXPLAIN SELECT * FROM user_info T1 
INNER JOIN user_agent T2 ON T1.id = T2.id 
WHERE T1.create_time>='2019-06-01 00:00:00' AND T1.create_time <='2019-06-20 23:59:59' 
AND T2.shop_level=1

EXPLAIN執行分析結果如下:

索引使用情況在possible_keys、key和key_len三列。每個字段代表的含義分別爲:

1) id

id列表示執行順序,id越大則越先執行,id相同則由上至下執行。

例:

EXPLAIN SELECT * FROM user_info WHERE id = (SELECT id from user_agent WHERE agent_level =1 LIMIT 1);

2) select_type

     select_type列提供了各種表示table列引用的使用方式的類型。最常見的值包括SIMPLE、PRIMARY、DERIVED 和UNION。其他可能的值還有UNION RESULT、DEPENDENT SUBQUERY、DEPENDENT UNION、UNCACHEABLE UNION 以及UNCACHEABLE QUERY。

       1、SIMPLE

對於不包含子查詢和其他複雜語法的簡單查詢,這是一個常見的類型。

例:

EXPLAIN SELECT T1.* FROM user_info T1;

       2、PRIMARY

這是爲更復雜的查詢而創建的首要表(也就是最外層的表)。這個類型通常可以在DERIVED 和UNION 類型混合使用時見到。

例:EXPLAIN SELECT * FROM (SELECT * FROM user_info WHERE nick_name = '東方不敗') T1

UNION

SELECT T1.* FROM user_info T1 WHERE T1.id > 5 AND T1.id<10;

        3、DERIVED

當一個表不是一個物理表時,那麼就被叫做DERIVED。下面的SQL語句給出了一個QEP中select-type爲DERIVED類型。

例:EXPLAIN SELECT * FROM (SELECT MAX(T1.id) FROM (SELECT id FROM user_info WHERE nick_name = '東方不敗') T1) T2;

        4、DEPENDENT SUBQUERY

這個select-type 值是爲使用子查詢而定義的。下面的SQL語句提供了這個值:

例:EXPLAIN SELECT T1.* FROM user_info T1 WHERE T1.id NOT IN (SELECT id FROM user_agent WHERE agent_level=1);

       5、UNION

這是UNION 語句其中的一個SQL 元素。

      6、UNION RESULT

這是一系列定義在UNION語句中的表的返回結果。當select_type爲這個值時,經常可以看到table的值是<unionN,M>,這說明匹配的id行是這個集合的一部分。下面的SQL產生了一個UNION和UNION RESULT。      

例:

EXPLAIN SELECT T1.* FROM user_info T1 WHERE T1.nick_name LIKE '東方不敗%'
        UNION
        SELECT T1.* FROM user_info T1 WHERE T1.id > 5 AND T1.id<10;

3) table

table列是EXPLAIN命令輸出結果中的一個單獨行的唯一標識符。這個值可能是表名、表的別名或者一個爲查詢產生臨時表的標識符,如派生表、子查詢或集合。

       例:EXPLAIN SELECT * FROM (SELECT MAX(T1.id) FROM (SELECT TU.id FROM user_info TU,user_agent TA WHERE TU.id=TA.id AND TU.nick_name LIKE '東方不敗%' AND TA.vip_num>=20) T1) T2;

       上圖中可以看出,id=1的表[derived2]表示爲id=2的表[TU]和TA衍生出來的表。

4) type

type列代表QEP(執行計劃)中指定的表使用的連接方式。type字段比較重要,它提供了判斷查詢是否高效的重要依據依據。 通過type字段,我們判斷此次查詢是全表掃描 還是索引掃描等。下面是最常用的幾種連接方式:

先從最佳類型到最差類型:NULL > system > const > eq_ref > ref > range > Index > All。ALL 類型因爲是全表掃描,因此在相同的查詢條件下,它是速度最慢的。而index類型的查詢雖然不是全表掃描,但是它掃描了所有的索引,因此比ALL類型的稍快,前面的幾種類型都是利用了索引來查詢數據,因此可以過濾部分或大部分數據,因此查詢效率就比較高了

       1、NULL在優化過程中就已得到結果,不用再訪問表或索引。

例:EXPLAIN SELECT MAX(id) FROM user_info

        2、system: 表中只有一條數據,這個類型是特殊的const類型,這種情況很少見。

       例:EXPLAIN SELECT * FROM (SELECT MAX(T1.id) FROM (SELECT TU.id FROM user_info TU WHERE TU.nick_name LIKE '東方不敗%') T1) T2;

        3、const: 針對主鍵或索引的等值查詢掃描,最多隻返回一行數據。const查詢速度非常快,因爲它僅僅讀取一次即可。例如下面的這個查詢,它使用了主鍵索引,因此type就是const類型的。

       例:EXPLAIN SELECT * FROM user_info WHERE id=1

        4、eq_ref: 此類型通常出現在多表的join查詢,表示對於前表的每一個結果,都只能匹配到後表的一行結果。並且查詢的比較操作通常是=,查詢效率較高。

       例:EXPLAIN SELECT * FROM user_info, user_agent WHERE user_info.id = user_agent.id AND user_info.id<100;

        5、ref: 此類型通常出現在多表的 join查詢,針對於非或非主鍵索引,或者是使用了最左前綴的規則索引的查詢。例如下面這個例子中,就使用到了ref類型的查詢:

       例:EXPLAIN SELECT * FROM user_info, user_agent WHERE user_info.id = user_agent.id AND user_agent.agent_level=1;

        6、range: 表示使用索引範圍查詢,通過索引字段範圍獲取表中部分數據記錄。這個類型通常出現在 =, <>, >, >=, <, <=, IS NULL, <=>, BETWEEN, IN() 操作中。例如下面的例子就是一個範圍查詢:

       例:EXPLAIN SELECT * FROM user_info WHERE id>=1 AND id<=100;

        7、index: 表示全索引掃描(full index scan),和ALL類型類似,只不過 ALL類型是全表掃描,而index類型則僅僅掃描所有的索引,而不掃描數據。index類型通常出現在:所要查詢的數據直接在索引樹中就可以獲取到, 而不需要掃描數據,通常就是查詢索引列, 並不是在WHERE條件後面使用索引列。當是這種情況時,Extra字段會顯示Using index。

       例:EXPLAIN SELECT nick_name FROM user_info;

        8、ALL: 表示全表掃描,這個類型的查詢是性能最差的查詢之一。通常來說,我們的查詢不應該出現ALL類型的查詢,因爲這樣的查詢在數據量大的情況下,對數據庫的性能是巨大的災難。 如一個查詢是ALL類型查詢, 那麼一般來說可以對相應的字段添加索引來避免。

      例:EXPLAIN SELECT * FROM user_info WHERE real_name ='TEST';

​​​​​​​​​​​​​​5)possible_keys

它表示mysql在查詢時,可能使用到的索引。 注意,即使有些索引在possible_keys中出現,但是並不表示此索引會真正地被mysql使用到。mysql在查詢時具體使用了哪些索引,由key字段決定。

6)key

key列指出mysql優化器決定選擇使用的索引來優化對該表的訪問。一般來說SQL查詢中的每個表都僅使用一個索引。也存在索引合併的少數例外情況,如給定表上用到了兩個或者更多索引。此字段是mysql在當前查詢時所真正使用到的索引。

7)key_len

定義了mysql在索引裏使用的字節數,表示查詢優化器使用了索引的字節數,這個字段可以評估組合索引是否完全被使用。

例:EXPLAIN SELECT nick_name FROM user_info WHERE id = 5610286981397000001;

注:key_len表示索引字段最大可能使用的長度,id是long類型,long類型的長度是8個字節,所以key_len=8。

8)ref

這個表示顯示索引的哪一列被使用了,如果可能的話,是一個常量。前文的type屬性裏也有ref,注意區別。

9)  rows

       MYSQL估計爲了找到所需的行而要讀取的行數。這個數字是內嵌循環關聯計劃裏的循環數目,也就是說它不是MYSQL認爲它最終要從表裏讀取出來的行數,而是MYSQL爲了找到符合查詢的每一點上標準的那些行而必須讀取的行的平均數。rows 列提供了試圖分析所有存在於累計結果集中的行數目的MySQL 優化器估計值。.rows是一個重要的字段,MYSQL查詢優化器根據統計信息,估算SQL要查找到結果集需要掃描讀取的數據行數,這個值非常直觀的顯示SQL效率好壞,原則上rows越少越好。可以對比key中的例子,一個沒建立索引錢,rows是9,建立索引後,rows是4。

10)filtered

在mysql5.1裏新加的,在使用explain extended時出現。它顯示的是針對表裏符合條件的記錄數的百分比所做的一個悲觀估算值。

  filtered 列給出了一個百分比的值,這個百分比值和rows 列的值相乘,可以估計出那些將要和QEP(執行計劃)中的前一個表進行連接的行的數目。前一個表就是指id 列的值比當前表的id 小的表。這一列只有在EXPLAIN EXTENDED 語句中才會出現。​​​​​​​

11)Extra

顯示上述信息之外的其它信息,但卻很重要。Extra 列可以包含多個值,可以有很多不同的取值,並且這些值還在隨着MySQL 新版本的發佈而進一步增加

例:EXPLAIN SELECT * FROM (SELECT MAX(T1.id) FROM (SELECT TU.id FROM user_info TU,user_agent TA WHERE TU.id=TA.id AND TU.nick_name LIKE '東方不敗%' AND TA.vip_num>=20) T1) T2;

using filesort說明MySQL會對數據使用一個外部的索引排序,而不是按照表內的索引順序進行讀取。MySQL中無法利用索引完成的排序操作稱爲“文件排序”,其實不一定是文件排序,內部使用的是快排序。一般有using filesort都建議優化去掉,因爲這樣的查詢cpu資源消耗大。

using temporary使用了臨時表保存中間結果,MySQL在對查詢結果排序時使用臨時表。常見於排序order by和分組查詢group by

using index表示相應的SELECT操作中使用了覆蓋索引(Covering Index),避免訪問了表的數據行,效率不錯。

using where表示MySQL服務器從存儲引擎收到行後再進行“後過濾”(Post-filter)。所謂“後過濾”,就是先讀取整行數據,再檢查此行是否符合where句的條件,符合就留下,不符合便丟棄。因爲檢查是在讀取行後才進行的,所以稱爲“後過濾”。。

select tables optimized away在沒有GROUP BY子句的情況下基於索引優化MIN/MAX操作或者對於MyISAM存儲引擎優化COUNT(*)操作,不必等到執行階段再進行計算,查詢執行計劃生成的階段即完成優化

distinct優化distinct操作,在找到第一匹配的元祖後即停止找同樣值的操作

1.5、索引失效的原因

1)對索引列運算,運算包括(+、-、*、/、!、<>、%、like'%_'(%放在前面);

2)類型錯誤,如字段類型爲varchar,where條件用number;

3)對索引應用內部函數,這種情況下應該建立基於函數的索引,如

SELECT * FROM table T1 WHERE ROUND(T1.logicdb_id) = 1,此時應該建ROUND(T1.logicdb_id)爲索引,mysql8.0開始支持函數索引,5.7可以通過虛擬列的方式來支持,之前只能新建一個ROUND(T1.logicdb_id)列然後去維護;

3)如果條件有OR,即使其中有條件帶索引也不會使用(這也是爲什麼建議少使用OR的原因),如果想使用OR,又想索引有效,只能將OR條件中的每個列加上索引

4)如果列類型是字符串,那一定要在條件中數據使用引號,否則不使用索引;

5)B-Tree索引IS NULL不會走索引,IS NOT NULL會走索引,對於位圖索引IS NULL,IS NOT NULL都會走;

6)組合索引遵循最左原則;

7)條件範圍查詢索引不生效的原因,如果MYSQL估計使用全表掃描要比使用索引快,則不使用索引,即查詢滿足條件的數據量接近數據表的數據量時,索引失效。

例如:EXPLAIN SELECT * FROM sys_connect WHERE create_time >= '2019-06-22 00:00:00' AND create_time<='2019-06-22 23:59:59';

1.6 字段類型和編碼

1)MYSQL返回字符串長度:CHARACTER_LENGTH方法(與CHAR_LENGTH一樣)返回的是字符數,LENGTH函數返回的是字節數,一個漢字三個字節;

2) varvhar等字段建立索引長度計算語句:select count(distinct left(test,5))/count(*) from table; 越趨近1越好;

3) MYSQL的utf8最大是3個字節不支持emoji表情符號,若支持emoji表情符號,必須使用utf8mb4。需要在MYSQL配置文件中配置客戶端字符集爲utf8mb4。jdbc的連接串不支持配置characterEncoding=utf8mb4,最好的辦法是在連接池中指定初始化SQL;

4) MYSQL排序規則(一般使用_bin和_genera_ci):

①:utf8_genera_ci不區分大小寫,ci爲case insensitive的縮寫,即大小寫不敏感;

②:utf8_general_cs區分大小寫,cs爲case sensitive的縮寫,即大小寫敏感,但是目前MySQL版本中已經不支持類似於***_genera_cs的排序規則,直接使用utf8_bin替代;

③:utf8_bin將字符串中的每一個字符用二進制數據存儲,區分大小寫。

1.7、索引的優缺點

1)索引的優點

可以大大加快數據的檢索速度,這也是創建索引的最主要的原因。且通過使用索引,可以在查詢的過程中,使用優化隱藏器,提高系統的性能。

2) 索引的缺點

索引需要額外的維護成本,因爲索引文件是單獨存在的文件,對數據的增加、修改、刪除都會產生額外的對索引文件的操作,這些操作需要消耗額外的IO,會降低增、改、刪的執行效率。

二、MYSQL分頁LIMIT查詢速度的優化

在MYSQL中LIMIT可以實現快速分頁,但是如果數據到幾百萬以上時,使用LIMIT必須優化纔能有效的合理的實現分頁,否則可能會拖垮服務器。當一個表數據有幾百萬的數據的時候LIMIT分頁就出現了性能問題問題!

例如:使用LIMIT分頁獲取用戶信息,查詢的SQL語句如下:

SELECT * FROM user_info LIMIT 0,20

當查詢第一頁的時間,查詢速度很快,也就幾十毫秒的事情,而當LIMIT 2000000,20的時候就很忙了。

所以,在使用LIMIT進行分頁查詢了,要巧用LIMIT的分頁的一些技巧。

2.1 借用書籤進行快速分頁

       所謂的書籤,是指能夠快速定位到某一條數據的所在位置。我們設計的每一張表都有一個主鍵id,可以把id當做一個書籤。每次獲取分頁數據後,記錄當前分頁的最大id,下次分頁時使用這個id進行過濾機分頁。

       例:SELECT * FROM user_info WHERE id> 890113247 LIMIT 0,20

       這樣的SQL語句查詢效率是非常快的,跟查詢第一頁的效率相當。

2.2 子查詢優化

       子查詢優化,是隻先找出第一條數據,然後再查詢大於等於這條數據的id就是要獲取的數據。子查詢優化的缺點是:數據必須是連續的,可以說不能有WHERE條件,WHERE條件會篩選數據,導致數據失去連續性。

       例:SELECT * FROM user_info WHERE id >= (SELECT id FROM user_info LIMIT 2000000,1) limit 20

查詢一下EXPLAIN結果:

2.3 延遲關聯

       我們項目中的一個關於用戶資產流水的分頁查詢,當用戶資產流水很多時,分頁就會很忙。

       例:查詢用戶資產流水的SQL語句如下:      

SELECT id,user_id,
              from_user_id,
              order_no,
              asset_type,
              label,
              balance,
              balance_result,
              type,
              COMMENT,
              create_time
FROM  asset_detail
WHERE  user_id = 1889 AND type IN (1,2,8,11,12,5,4,2,3,7,6,13,14,51,102)
ORDER BY create_time DESC
       LIMIT 0,20

 EXPLAIN分析結果:

       上述的查詢語句中,也走了索引,但實際查詢還是非常慢,其中慢的原因在於使用的倒序排序,整個查詢結果將近需要60秒的時間。

       經過優化的SQL如下:     

  SELECT
       T1.*
FROM (
       SELECT
              id,
              user_id,
              from_user_id,
              order_no,
              asset_type,
              label,
              balance,
              balance_result,
              type,
              COMMENT,
              create_time
       FROM
              asset_detail
       WHERE
              1 = 1
         AND user_id = 1867
              AND type IN (201,202,8,11,12,5,4,2,3,7,6,13,14,51,102)
       ) T1
       INNER JOIN asset_detail T2 ON T1.id = T2.id
ORDER BY
       T2.create_time DESC
       LIMIT 0,20

       EXPLAIN分析結果:

       經過優化後的SQL,查詢用了不到1秒中的時間。

三、MYSQ優化的一些建議

       MYSQL查詢優化的一些常用的建議點,如下:

  1. EXPLAIN

做MySQL優化,我們要善用 EXPLAIN 查看SQL執行計劃。

2. SQL語句中IN包含的值不應過多

       MySQL對於IN做了相應的優化,即將IN中的常量全部存儲在一個數組裏面,而且這個數組是排好序的。但是如果數值較多,產生的消耗也是比較大的,建議控制在1000以內。

例如:對於IN內的連續數值查詢,SELECT * FROM user_info WHERE id IN(1,2,3,4,5,6,7,8,9,10),能用 BETWEEN就不要用IN了;再或者使用連接來替換。

3.SELECT語句務必指明字段名稱

       SELECT * FROM增加很多不必要的消耗(CPU、IO、內存、網絡帶寬);增加了使用覆蓋索引的可能性;當表結構發生改變時,前端也需要更新。所以建議直接在SELECT後面接上字段名。

4. 獲取唯一一條數據的時候,請使用LIMIT1

       使用LIMIT 1,是爲了使EXPLAIN中type列達到const類型,提高查詢效率。

5. 如果排序字段沒有用到索引,就儘量少排序

6. 如果限制條件中其他字段沒有索引,儘量少用OR

OR兩邊的字段中,如果有一個不是索引字段,而其他條件也不是索引字段,會造成該查詢不走索引的情況。在很多時候使用 UNION ALL或者是UNION的方式來代替“OR”會得到更好的效果。

7. 建議使用UNION ALL代替UNION

       UNION和UNION ALL的差異主要是前者需要將結果集合並後再進行唯一性過濾操作,這就會涉及到排序,增加大量的CPU運算,加大資源消耗及延遲。使用UNION ALL的前提條件是兩個結果集沒有重複數據。

8. 不使用ORDER BY RAND()

       比如說,隨機查詢100個用戶信息,SQL語句如下:

       SELECT * FROM user_info ORDER BY RAND() LIMIT 100;

       上面的SQL語句可以優化爲:

       SELECT * FROM user_info T1 INNER JOIN (SELECT RAND() * (SELECT MAX(id) FROM user_info) AS rand_id) T2 ON T1.id > T2.rand_id LIMIT 100;

9. 區分IN和EXISTS,NOT IN和NOT EXISTS

       區分IN和EXISTS主要是造成了驅動順序的改變(這是性能變化的關鍵),如果是EXISTS,那麼以外層表爲驅動表先被訪問,如果是IN,那麼先執行子查詢。所以IN適合於外表大而內表小的情況;EXISTS適合於外表小而內表大的情況。

關於NOT IN和NOT EXISTS,推薦使用NOT EXISTS,不僅僅是效率問題,NOTIN可能存在邏輯問題。如何高效的寫出一個替代NOT EXISTS的SQL語句?

       使用NOT EXISTS統計表A中的id在表B中不存在的數據總數的SQL語句:

       SELECT COUNT(*) FROM user_info A WHERE NOT EXISTS (SELECT B.id FROM user_agent B WHERE A.id=B.id);

       優化後的SQL語句:

SELECT COUNT(*) FROM user_info A LEFT JOIN user_agent B ON A.id = B.id WHERE B.id IS NULL;

10. 使用合理的分頁方式以提高分頁的效率

       例:SELECT * FROM user_info LIMIT 2000000,20;

使用上述SQL語句做分頁的時候,我們會發現,隨着表數據量的不斷增加,直接使用LIMIT分頁查詢會越來越慢。優化的方法是:可以藉助表的id唯一主鍵索引的特性,取前一頁的最大行數的id,然後根據這個最大的id來限制下一頁的起點。

SELECT * FROM user_info WHERE id>890113272 LIMIT 20;

11. 分段查詢

在一些用戶選擇條件查詢的頁面中,可能一些用戶選擇的時間範圍過大,造成查詢緩慢。主要的原因是掃描行數過多。這個時候可以通過程序,分段進行查詢,循環遍歷,將結果合併處理進行展示。

例:EXPLAIN SELECT * FROM user_info WHERE id BETWEEN 1000000 AND 2000000;

     對於上述的這條SQL的查詢,掃描的行數接近百萬級以上的時候就可以使用分段查詢。

12. 避免在WHERE子句中對字段進行NULL值判斷

       對於NULL的判斷會導致引擎放棄使用索引而進行全表掃描,大幅降低了查詢效率。

13. 不建議使用%前綴模糊查詢

       例如:LIKE “%name”或者LIKE “%name%”,這種查詢會導致索引失效而進行全表掃描。但是可以使用LIKE “name%”。

14. 避免在WHERE子句中對字段進行表達,函數式操作

       例:SELECT * FROM user_info WHERE vip_number*2=36

上述的查詢語句中對字段就行了算術運算,這會造成引擎放棄使用索引,應該改成:

SELECT * FROM user_info WHERE vip_number=36*2

15. 避免隱式類型轉換

       WHERE子句中出現 COLUMN字段的類型和傳入的參數類型不一致的時候發生的類型轉換,建議先確定WHERE中的參數類型。

16. 對於聯合索引來說,要遵守最左前綴法則

舉列來說索引含有字段id,name,time,可以直接用id字段,也可以id,name這樣的順序,但是name,time都無法使用這個索引。所以在創建聯合索引的時候一定要注意索引字段順序,常用的查詢字段放在最前面。

17. 必要時可以使用FORCE INDEX來強制查詢走某個索引

有的時候MYSQL優化器採取它認爲合適的索引來檢索SQL語句,但是可能它所採用的索引並不是我們想要的。這時就可以採用FORCE INDEX來強制優化器使用我們制定的索引。

18. 注意範圍查詢語句

對於聯合索引來說,如果存在範圍查詢,比如BETWEEN,>,<等條件時,會造成後面的索引字段失效。

19. 關於JOIN優化

       LEFT JOIN A表爲驅動表;

INNER JOIN MySQL會自動找出那個數據少的表作用驅動表;

RIGHT JOIN B表爲驅動表;

儘量使用INNER JOIN,避免LEFT JOIN:

參與聯合查詢的表至少爲2張表,一般都存在大小之分。如果連接方式是INNER JOIN,在沒有其他過濾條件的情況下MySQL會自動選擇小表作爲驅動表,但是LEFT JOIN在驅動表的選擇上遵循的是左邊驅動右邊的原則,即LEFT JOIN左邊的表名爲驅動表。

合理利用索引

被驅動表的索引字段作爲on的限制字段。

利用小表去驅動大表

20. 保證每張表都有一個主鍵ID

養成一種良好的習慣,每設計一張新表的時候,都應該爲其設計一個ID字段,並讓其成爲主鍵,而且最好是整型,同時設置這個ID字段爲自增(AUTO_INCREMENT)的標誌。

21. 選擇正確的存儲引擎

       MYSQL中的兩個主要存儲引擎是InnoDB和MYISAM。每個人都有自己的優點和缺點。

InnoDB是一個更復雜的存儲引擎,在大多數小型應用程序中可能比MYISAM慢,但是它支持基於行的鎖定,這樣可以更好地擴展。它還支持一些更高級的特性,如事務。

MYISAM很適合閱讀量大的應用程序,但是當有大量的寫操作時,它的擴展就不太好了。即使您正在更新一行中的一個字段,整個表也會被鎖定,在該查詢完成之前,任何其他進程都無法從中讀取。MYISAM在計算SELECT(*)類型查詢方面非常快速。

四、數據庫性能的一些思考

在對慢SQL查詢做優化的時候,很多時候可能是忘了建索引,像這種問題很容易解決,加個索引就行了。但是有一下幾種情況就不是簡單能加索引能解決了。

4.1、業務代碼循環讀數據庫

考慮這樣一個場景,獲取用戶粉絲列表信息 加入分頁是10個,其實像這樣的SQL是十分簡單的,通過連表查詢性能也很高,但是有的時候,很多開發採用了取出一串id,然後循環讀每個id的信息,這樣如果id很多對數據庫的壓力是很大的,而且性能也很低。

4.2、統計SQL

很多時候,業務上都會有排行榜統計,發現開發有很多地方直接採用數據庫做計算,在對一些大表的做聚合運算的時候,經常超過五秒,這些SQL一般很長而且很難優化。像這種場景,如果業務允許(比如一致性要求不高或者是隔一段時間才統計的),可以專門在從庫裏面做統計。

4.3、超大分頁

在慢SQL查詢日誌中發現了一些超大分頁的慢查詢如LIMIT 4000000,1000,因爲MYSQL的分頁是在server層做的,可以採用延遲關聯再減少回表。但是看了相關的業務代碼,正常的業務邏輯是不會出現這樣的請求的,這種情況很有可能是有惡意用戶在刷接口。所以最好在開發的時候也對接口加上校驗攔截這些惡意請求。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章