MySQL索引和查詢優化集合

索引相關

1.索引基數:

      基數指的是數據庫i額所包含的不同值的數量,例如 某個數據列包含值 1、3、7、4、7、3,那麼它的基數就是 4。

索引的技術相對於數據錶行數較高的時候,工作效率更高。即重複值越少,執行效率越好。

      如果某數據列含有很多不同的年齡,索引會很快地分辨數據行;如果某個數據列用於記錄性別(只有“M”和“F”兩種值),那麼索引的用處就不大;如果值出現的機率幾乎相等,那麼無論搜索哪個值都可能得到一半的數據行。

      在這些情況下,最好根本不要使用索引,因爲查詢優化器發現某個值出現在表的數據行中的百分比很高的時候,它一般會忽略索引,進行全表掃描。慣用的百分比界線是“30%”。

2.索引失效的原因:

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

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

      3)對索引應用內部函數,這種情況下應該要建立基於函數的索引。例如 select * from template t where ROUND (t.logicdb_id)       = 1,此時應該建 ROUND (t.logicdb_id) 爲索引。

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

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

      6)B-tree 索引 is null 不會走,is not null 會走,位圖索引 is null,is not null 都會走。

      7)組合索引遵循最左原則。

3.索引的建立需要注意以下幾點:

  • 最重要的肯定是根據業務經常查詢的語句。

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

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

EXPLAIN 相關

      通過explain可以查看SQL語句的執行計劃。explain select * from user

  • desc 或者 explain 加上你的 SQL。

  • explain extended 加上你的 SQL,然後通過 show warnings 可以查看實際執行的語句,這一點也是非常有用的,很多時候不同的寫法經 SQL 分析後,實際執行的代碼是一樣的

  • --------------------------------------------------------------------------------------------------------------------------------------------------------------

參考地址:https://zhuanlan.zhihu.com/p/48385127

一,SQL語句性能優化
1, 對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。

   經測試,效率明顯提高

2,應儘量避免在 where 子句中對字段進行 null 值判斷,創建表時NULL是默認值,但大多數時候應該使用NOT NULL,或者使用一個特殊的值,如0,-1作爲默 認值。

     ( 原因1:要儘可能地把字段定義爲 NOT NULL。即使應用程序無須保存 NULL(沒有值),也有許多表包含了可空列(Nullable Column),這僅僅是因爲它爲默認選項。除非真的要保存 NULL,否則就把列定義爲 NOT NULL。

    MySQL難以優化引用了可空列的查詢,它會使索引、索引統計和值更加複雜。可空列需要更多的儲存空間,還需要在MySQL內部進行特殊處理。當可空列被索引的時候,每條記錄都需要一個額外的字節,還可能導致 MyISAM 中固定大小的索引(例如一個整數列上的索引)變成可變大小的索引。 

    即使要在表中儲存「沒有值」的字段,還是有可能不使用 NULL 的。考慮使用 0、特殊值或空字符串來代替它。 

把 NULL 列改爲 NOT NULL 帶來的性能提升很小,所以除非確定它引入了問題,否則就不要把它當作優先的優化措施。然後,如果計劃對列進行索引,就要儘量避免把它設置爲可空。

原因2:使索引無效。

   真實原因不詳,應該是第一個)

   2 經測試:感覺效率差不多,可能新版已經優化了。

3,應儘量避免在 where 子句中使用!=或<>操作符 

      使用Navicat測試,感覺效率差不多  。

4,應儘量避免在 where 子句中使用 or 來連接條件, 否則將導致引擎放棄使用索引而進行全表掃描, 可以 使用UNION合併查詢: select id from t where num=10 union all select id from t where num=20

    經測試,效率相似

5,in 和 not in 也要慎用,否則會導致全表掃描,對於連續的數值,能用 between 就不要用 in 了:Select id from t where num between 1 and 3

   經測試,效率相似

6,下面的查詢也將導致全表掃描:select id from t where name like ‘%abc%’ 或者select id from t where name like ‘%abc’若要提高效率,可以考慮全文檢索。而select id from t where name like ‘abc%’ 纔用到索引

     經測試,效率明顯提升

7, 如果在 where 子句中使用參數,也會導致全表掃描。

8,應儘量避免在 where 子句中對字段進行表達式操作,應儘量避免在where子句中對字段進行函數操作

9,  很多時候用 exists 代替 in 是一個好的選擇: select num from a where num in(select num from b).用下面的語句替換: select num from a where exists(select 1 from b where num=a.num)

10,  索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因爲 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過5個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。

11,  應儘可能的避免更新 clustered 索引數據列, 因爲 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那麼需要考慮是否應將該索引建爲 clustered 索引。--- 未驗證

12,儘量使用數字型字段,若只含數值信息的字段儘量不要設計爲字符型,這會降低查詢和連接的性能,並會增加存儲開銷。

13,儘可能的使用 varchar/nvarchar 代替 char/nchar , 因爲首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。

14,最好不要使用返回所有: select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。(正解)

15,儘量避免向客戶端返回大數據量,若數據量過大,應該考慮相應需求是否合理。

16,使用表的別名(Alias):當在SQL語句中連接多個表時,請使用表的別名並把別名前綴於每個Column上.這樣一來,就可以減少解析的時間並減少那些由Column歧義引起的語法錯誤。

17,使用(慎用)“臨時表”暫存中間結果
   簡化SQL語句的重要方法就是採用臨時表暫存中間結果,但是,臨時表的好處遠遠不止這些,將臨時結果暫存在臨時表,後面的查詢就在tempdb中了,這可以避免程序中多次掃描主表,也大大減少了程序執行中“共享鎖”阻塞“更新鎖”,減少了阻塞,提高了併發性能。

 注意:臨時表在處理少量數據中性能優異     

舉例說明:
SELECT
  b.*,
  A.value1
FROM table1 b
  LEFT JOIN table2A
    ON b.order_no = A.order_no AND b.channel_no = A.channel_no
WHERE 1 = 1 AND EXISTS (SELECT
    1
  FROM table3 t
  WHERE b.order_no = t.order_no AND t.ticket_no LIKE '%1792903240%')
ORDER BY CREATE_TIME LIMIT 0, 20 

主要思路就是 先從200W+ 的table3中查出來order_no 然後把order_no插入臨時表,然後再使用in 臨時表查詢,減少關聯掃描次數就能極大的優化查詢時間

前提: table3中的ticket_no 重複率非常低,200W+的數據 有200W的非重複,爲什麼強調這個,臨時表在處理少量數據時性能很優異,(一般只在確定不能用索引的時候才使用臨時表,或者在存儲過程中某些固定數據使用次數非常多的時候使用臨時表,其他時候一般不建議使用)

18,一些SQL查詢語句應加上nolock,讀、寫是會相互阻塞的,爲了提高併發性能,對於一些查詢,可以加上nolock,這樣讀的時候可以允許寫,但缺點是可能讀到未提交的髒數據。使用 nolock有3條原則。查詢的結果用於“插、刪、改”的不能加nolock !查詢的表屬於頻繁發生頁分裂的,慎用nolock !使用臨時表一樣可以保存“數據前影”,起到類似Oracle的undo表空間的功能,能採用臨時表提高併發性能的,不要用nolock 。-- ??

19,常見的簡化規則如下:不要有超過5個以上的表連接(JOIN),考慮使用臨時表或表變量存放中間結果。少用子查詢,視圖嵌套不要過深,一般視圖嵌套不要超過2個爲宜。

20,將需要查詢的結果預先計算好放在表中,查詢的時候再Select。這在SQL7.0以前是最重要的手段。例如醫院的住院費計算。--??

21,用OR的字句可以分解成多個查詢,並且通過UNION 連接多個查詢。他們的速度只同是否使用索引有關,如果查詢需要用到聯合索引,用UNION all執行的效率更高.多個OR的字句沒有用到索引,改寫成UNION的形式再試圖與索引匹配。一個關鍵的問題是否用到索引。--重複

22,在IN後面值的列表中,將出現最頻繁的值放在最前面,出現得最少的放在最後面,減少判斷的次數。

    --經測試,相似

23,儘量將數據的處理工作放在服務器上,減少網絡的開銷,如使用存儲過程。存儲過程是編譯好、優化過、並且被組織到一個執行規劃裏、且存儲在數據庫中的SQL語句,是控制流語言的集合,速度當然快。反覆執行的動態SQL,可以使用臨時存儲過程,該過程(臨時表)被放在Tempdb中。

24,當服務器的內存夠多時,配製線程數量 = 最大連接數+5,這樣能發揮最大的效率;否則使用 配製線程數量<最大連接數啓用SQL SERVER的線程池來解決,如果還是數量 = 最大連接數+5,嚴重的損害服務器的性能。

25,查詢的關聯同寫的順序
 

select a.personMemberID, *
from chineseresume a,
personmember b where a.personMemberID = b.referenceid and a.personMemberID = ‘JCNPRH39681’ 
(A = B ,A = ‘號碼’)

select a.personMemberID, * 
from chineseresume a,
personmember b 
where a.personMemberID = b.referenceid 
and a.personMemberID = ‘JCNPRH39681’ 
and b.referenceid = ‘JCNPRH39681’ 
(A = B ,A = ‘號碼’, B = ‘號碼’)

select a.personMemberID, * 
from chineseresume a,
personmember b 
where b.referenceid = ‘JCNPRH39681’ 
and a.personMemberID = ‘JCNPRH39681’ 
(B = ‘號碼’, A = ‘號碼’)

26,儘量使用exists代替select count(1)來判斷是否存在記錄,count函數只有在統計表中所有行數時使用,而且count(1)比count(*)更有效率。

27,儘量使用“>=”,不要使用“>”。測試,感覺差不多

28,索引的使用規範:索引的創建要與應用結合考慮,建議大的OLTP表不要超過6個索引;儘可能的使用索引字段作爲查詢條件,尤其是聚簇索引,必要時可以通過index index_name來強制指定索引;避免對大表查詢時進行table scan,必要時考慮新建索引;在使用索引字段作爲條件時,如果該索引是聯合索引,那麼必須使用到該索引中的第一個字段作爲條件時才能保證系統使用該索引,否則該索引將不會被使用;要注意索引的維護,週期性重建索引,重新編譯存儲過程。  

29,下列SQL條件語句中的列都建有恰當的索引,但執行速度卻非常慢:
SELECT * FROM record WHERE substrINg(card_no,1,4)=’5378’ (13秒)
SELECT * FROM record WHERE amount/30< 1000 (11秒)
SELECT * FROM record WHERE convert(char(10),date,112)=’19991201’ (10秒)
分析:
WHERE子句中對列的任何操作結果都是在SQL運行時逐列計算得到的,因此它不得不進行表搜索,而沒有使用該列上面的索引;如果這些結果在查詢編譯時就能得到,那麼就可以被SQL優化器優化,使用索引,避免表搜索,因此將SQL重寫成下面這樣:
SELECT * FROM record WHERE card_no like ‘5378%’ (< 1秒)
SELECT * FROM record WHERE amount< 1000*30 (< 1秒)
SELECT * FROM record WHERE date= ‘1999/12/01’ (< 1秒)

30,當有一批處理的插入或更新時,用批量插入或批量更新,絕不會一條條記錄的去更新!

31,在所有的存儲過程中,能夠用SQL語句的,我絕不會用循環去實現!
32,選擇最有效率的表名順序(只在基於規則的優化器中有效):
    oracle 的解析器按照從右到左的順序處理FROM子句中的表名,FROM子句中寫在最後的表(基礎表 driving table)將被最先處理,在FROM子句中包含多個表的情況下,你必須選擇記錄條數最少的表作爲基礎表。如果有3個以上的表連接查詢, 那就需要選擇交叉表(intersection table)作爲基礎表, 交叉表是指那個被其他表所引用的表.

33,提高GROUP BY語句的效率, 可以通過將不需要的記錄在GROUP BY 之前過濾掉.下面兩個查詢返回相同結果,但第二個明顯就快了許多.
低效:
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

34,sql語句用大寫,因爲oracle 總是先解析sql語句,把小寫的字母轉換成大寫的再執行。

35,別名的使用,別名是大型數據庫的應用技巧,就是表名、列名在查詢中以一個字母爲別名,查詢速度要比建連接錶快1.5倍。

36,避免死鎖,在你的存儲過程和觸發器中訪問同一個表時總是以相同的順序;事務應經可能地縮短,在一個事務中應儘可能減少涉及到的數據量;永遠不要在事務中等待用戶輸入。

37,避免使用臨時表,除非卻有需要,否則應儘量避免使用臨時表,相反,可以使用表變量代替;大多數時候(99%),表變量駐紮在內存中,因此速度比臨時表更快,臨時表駐紮在TempDb數據庫中,因此臨時表上的操作需要跨數據庫通信,速度自然慢。

38,最好不要使用觸發器,觸發一個觸發器,執行一個觸發器事件本身就是一個耗費資源的過程;如果能夠使用約束實現的,儘量不要使用觸發器;不要爲不同的觸發事件(Insert,Update和Delete)使用相同的觸發器;不要在觸發器中使用事務型代碼。

39,索引創建規則:
表的主鍵、外鍵必須有索引;
數據量超過300的表應該有索引;
經常與其他表進行連接的表,在連接字段上應該建立索引;
經常出現在Where子句中的字段,特別是大表的字段,應該建立索引;
索引應該建在選擇性高的字段上;
索引應該建在小字段上,對於大的文本字段甚至超長字段,不要建索引;
複合索引的建立需要進行仔細分析,儘量考慮用單字段索引代替;
正確選擇複合索引中的主列字段,一般是選擇性較好的字段;
複合索引的幾個字段是否經常同時以AND方式出現在Where子句中?單字段查詢是否極少甚至沒有?如果是,則可以建立複合索引;否則考慮單字段索引;
如果複合索引中包含的字段經常單獨出現在Where子句中,則分解爲多個單字段索引;
如果複合索引所包含的字段超過3個,那麼仔細考慮其必要性,考慮減少複合的字段;
如果既有單字段索引,又有這幾個字段上的複合索引,一般可以刪除複合索引;
頻繁進行數據操作的表,不要建立太多的索引;
刪除無用的索引,避免對執行計劃造成負面影響;
表上建立的每個索引都會增加存儲開銷,索引對於插入、刪除、更新操作也會增加處理上的開銷。另外,過多的複合索引,在有單字段索引的情況下,一般都是沒有存在價值的;相反,還會降低數據增加刪除時的性能,特別是對頻繁更新的表來說,負面影響更大。
儘量不要對數據庫中某個含有大量重複的值的字段建立索引。

40,mysql查詢優化總結:使用慢查詢日誌去發現慢查詢,使用執行計劃去判斷查詢是否正常運行,總是去測試你的查詢看看是否他們運行在最佳狀態下。久而久之性能總會變化,避免在整個表上使用count(*),它可能鎖住整張表,使查詢保持一致以便後續相似的查詢可以使用查詢緩存
,在適當的情形下使用GROUP BY而不是DISTINCT,在WHERE, GROUP BY和ORDER BY子句中使用有索引的列,保持索引簡單,不在多個索引中包含同一個列,有時候MySQL會使用錯誤的索引,對於這種情況使用USE INDEX,檢查使用SQL_MODE=STRICT的問題,對於記錄數小於5的索引字段,在UNION的時候使用LIMIT不是是用OR。
爲了 避免在更新前SELECT,使用INSERT ON DUPLICATE KEY或者INSERT IGNORE ,不要用UPDATE去實現,不要使用 MAX,使用索引字段和ORDER BY子句,LIMIT M,N實際上可以減緩查詢在某些情況下,有節制地使用,在WHERE子句中使用UNION代替子查詢,在重新啓動的MySQL,記得來溫暖你的數據庫,以確保您的數據在內存和查詢速度快,考慮持久連接,而不是多個連接,以減少開銷,基準查詢,包括使用服務器上的負載,有時一個簡單的查詢可以影響其他查詢,當負載增加您的服務器上,使用SHOW PROCESSLIST查看慢的和有問題的查詢,在開發環境中產生的鏡像數據中 測試的所有可疑的查詢。

41,MySQL 備份過程:
從二級複製服務器上進行備份。在進行備份期間停止複製,以避免在數據依賴和外鍵約束上出現不一致。徹底停止MySQL,從數據庫文件進行備份。
如果使用 MySQL dump進行備份,請同時備份二進制日誌文件 – 確保複製沒有中斷。不要信任LVM 快照,這很可能產生數據不一致,將來會給你帶來麻煩。爲了更容易進行單表恢復,以表爲單位導出數據 – 如果數據是與其他表隔離的。
當使用mysqldump時請使用 –opt。在備份之前檢查和優化表。爲了更快的進行導入,在導入時臨時禁用外鍵約束。
爲了更快的進行導入,在導入時臨時禁用唯一性檢測。在每一次備份後計算數據庫,表以及索引的尺寸,以便更夠監控數據尺寸的增長。
通過自動調度腳本監控複製實例的錯誤和延遲。定期執行備份。

42,查詢緩衝並不自動處理空格,因此,在寫SQL語句時,應儘量減少空格的使用,尤其是在SQL首和尾的空格(因爲,查詢緩衝並不自動截取首尾空格)。

43,member用mid做標準進行分表方便查詢麼?一般的業務需求中基本上都是以username爲查詢依據,正常應當是username做hash取模來分表吧。分表的話 mysql 的partition功能就是幹這個的,對代碼是透明的;
在代碼層面去實現貌似是不合理的。

44,我們應該爲數據庫裏的每張表都設置一個ID做爲其主鍵,而且最好的是一個INT型的(推薦使用UNSIGNED),並設置上自動增加的AUTO_INCREMENT標誌。

45,在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。
無需在執行存儲過程和觸發器的每個語句後向客戶端發送 DONE_IN_PROC 消息。

46,MySQL查詢可以啓用高速查詢緩存。這是提高數據庫性能的有效Mysql優化方法之一。當同一個查詢被執行多次時,從緩存中提取數據和直接從數據庫中返回數據快很多。

47,EXPLAIN SELECT 查詢用來跟蹤查看效果
使用 EXPLAIN 關鍵字可以讓你知道MySQL是如何處理你的SQL語句的。這可以幫你分析你的查詢語句或是表結構的性能瓶頸。EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的數據表是如何被搜索和排序的……等等,等等。

48,當只要一行數據時使用 LIMIT 1
當你查詢表的有些時候,你已經知道結果只會有一條結果,但因爲你可能需要去fetch遊標,或是你也許會去檢查返回的記錄數。在這種情況下,加上 LIMIT 1 可以增加性能。這樣一樣,MySQL數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查少下一條符合記錄的數據。

49,選擇表合適存儲引擎:
myisam: 應用時以讀和插入操作爲主,只有少量的更新和刪除,並且對事務的完整性,併發性要求不是很高的。
Innodb: 事務處理,以及併發條件下要求數據的一致性。除了插入和查詢外,包括很多的更新和刪除。(Innodb有效地降低刪除和更新導致的鎖定)。對於支持事務的InnoDB類型的表來說,影響速度的主要原因是AUTOCOMMIT默認設置是打開的,而且程序沒有顯式調用BEGIN 開始事務,導致每插入一條都自動提交,嚴重影響了速度。可以在執行sql前調用begin,多條sql形成一個事物(即使autocommit打開也可以),將大大提高性能。

50,優化表的數據類型,選擇合適的數據類型:
原則:更小通常更好,簡單就好,所有字段都得有默認值,儘量避免null。
例如:數據庫表設計時候更小的佔磁盤空間儘可能使用更小的整數類型.(mediumint就比int更合適)
比如時間字段:datetime和timestamp, datetime佔用8個字節,而timestamp佔用4個字節,只用了一半,而timestamp表示的範圍是1970—2037適合做更新時間
MySQL可以很好的支持大數據量的存取,但是一般說來,數據庫中的表越小,在它上面執行的查詢也就會越快。
因此,在創建表的時候,爲了獲得更好的性能,我們可以將表中字段的寬度設得儘可能小。例如,
在定義郵政編碼這個字段時,如果將其設置爲CHAR(255),顯然給數據庫增加了不必要的空間,
甚至使用VARCHAR這種類型也是多餘的,因爲CHAR(6)就可以很好的完成任務了。同樣的,如果可以的話,
我們應該使用MEDIUMINT而不是BIGIN來定義整型字段。
應該儘量把字段設置爲NOT NULL,這樣在將來執行查詢的時候,數據庫不用去比較NULL值。
對於某些文本字段,例如“省份”或者“性別”,我們可以將它們定義爲ENUM類型。因爲在MySQL中,ENUM類型被當作數值型數據來處理,
而數值型數據被處理起來的速度要比文本類型快得多。這樣,我們又可以提高數據庫的性能。

51, 字符串數據類型:char,varchar,text選擇區別

52,任何對列的操作都將導致表掃描,它包括數據庫函數、計算表達式等等,查詢時要儘可能將操作移至等號右邊。

二、索引優化

1.對查詢進行優化,應儘量避免全表掃描,首先應考慮在 where 及 order by 涉及的列上建立索引。

2.應儘量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全表掃描,如:select id from t where num is null可以在num上設置默認值0,確保表中num列沒有null值,然後這樣查詢:select id from t where num=0

3.應儘量避免在 where 子句中使用!=或<>操作符,否則引擎將放棄使用索引而進行全表掃描。

4.應儘量避免在 where 子句中使用or 來連接條件,否則將導致引擎放棄使用索引而進行全表掃描,如:select id from t where num=10 or num=20可以這樣查詢:select id from t where num=10 union all select id from t where num=20

http://5.in 和 not in 也要慎用,否則會導致全表掃描,如:select id from t where num in(1,2,3) 對於連續的數值,能用 between 就不要用 in 了:select id from t where num between 1 and 3

6.下面的查詢也將導致全表掃描:select id from t where name like ‘李%’若要提高效率,可以考慮全文檢索。

7.如果在 where 子句中使用參數,也會導致全表掃描。因爲SQL只有在運行時纔會解析局部變量,但優化程序不能將訪問計劃的選擇推遲到運行時;它必須在編譯時進行選擇。然 而,如果在編譯時建立訪問計劃,變量的值還是未知的,因而無法作爲索引選擇的輸入項。如下面語句將進行全表掃描:select id from t where num=@num可以改爲強制查詢使用索引:select id from t with(index(索引名)) where num=@num

8.應儘量避免在 where 子句中對字段進行表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:select id from t where num/2=100應改爲:select id from t where num=100*2

9.應儘量避免在where子句中對字段進行函數操作,這將導致引擎放棄使用索引而進行全表掃描。如:select id from t where substring(name,1,3)=’abc’ ,name以abc開頭的id

應改爲:

select id from t where name like ‘abc%’

10.不要在 where 子句中的“=”左邊進行函數、算術運算或其他表達式運算,否則系統將可能無法正確使用索引。

11.在使用索引字段作爲條件時,如果該索引是複合索引,那麼必須使用到該索引中的第一個字段作爲條件時才能保證系統使用該索引,否則該索引將不會被使用,並且應儘可能的讓字段順序與索引順序相一致。

12.不要寫一些沒有意義的查詢,如需要生成一個空表結構:select col1,col2 into #t from t where 1=0

這類代碼不會返回任何結果集,但是會消耗系統資源的,應改成這樣:

create table #t(…)

13.很多時候用 exists 代替 in 是一個好的選擇:select num from a where num in(select num from b)

用下面的語句替換:

select num from a where exists(select 1 from b where num=a.num)

14.並不是所有索引對查詢都有效,SQL是根據表中數據來進行查詢優化的,當索引列有大量數據重複時,SQL查詢可能不會去利用索引,如一表中有字段sex,male、female幾乎各一半,那麼即使在sex上建了索引也對查詢效率起不了作用。

15.索引並不是越多越好,索引固然可 以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因爲 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有 必要。

16.應儘可能的避免更新 clustered 索引數據列,因爲 clustered 索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新 clustered 索引數據列,那麼需要考慮是否應將該索引建爲 clustered 索引。

17.儘量使用數字型字段,若只含數值信息的字段儘量不要設計爲字符型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因爲引擎在處理查詢和連接時會逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了。

18.儘可能的使用 varchar/nvarchar 代替 char/nchar ,因爲首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。

19.任何地方都不要使用 select * from t ,用具體的字段列表代替“*”,不要返回用不到的任何字段。

20.儘量使用表變量來代替臨時表。如果表變量包含大量數據,請注意索引非常有限(只有主鍵索引)。

21.避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。

22.臨時表並不是不可使用,適當地使用它們可以使某些例程更有效,例如,當需要重複引用大型表或常用表中的某個數據集時。但是,對於一次性事件,最好使用導出表。

23.在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,爲了緩和系統表的資源,應先create table,然後insert。

24.如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。

25.儘量避免使用遊標,因爲遊標的效率較差,如果遊標操作的數據超過1萬行,那麼就應該考慮改寫。

26.使用基於遊標的方法或臨時表方法之前,應先尋找基於集的解決方案來解決問題,基於集的方法通常更有效。

27.與臨時表一樣,遊標並不是不可使 用。對小型數據集使用 FAST_FORWARD 遊標通常要優於其他逐行處理方法,尤其是在必須引用幾個表才能獲得所需的數據時。在結果集中包括“合計”的例程通常要比使用遊標執行的速度快。如果開發時 間允許,基於遊標的方法和基於集的方法都可以嘗試一下,看哪一種方法的效果更好。

28.在所有的存儲過程和觸發器的開始處設置 SET NOCOUNT ON ,在結束時設置 SET NOCOUNT OFF 。無需在執行存儲過程和觸發器的每個語句後向客戶端發送DONE_IN_PROC 消息。

29.儘量避免大事務操作,提高系統併發能力。

上述優化方案,需自我辨別

MySQL EXPLAIN type類型說明

EXPLAIN執行計劃中type字段分爲以下幾種:ALL   INDEX   RANGE   REF   EQ_REF   CONST,SYSTEM    NULL

自上而下,性能從最差到最好。

type = ALL,全表掃描,MYSQL掃描全表來找到匹配的行

(因爲film表中rating不是索引)

mysql> explain extended select * from film where rating > 9\G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: film

         type: ALL

possible_keys: NULL

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 1024

     filtered: 100.00

        Extra: Using where

1 row in set, 1 warning (0.00 sec)

 

type = index,索引全掃描,MYSQL遍歷整個索引來查找匹配的行。(雖然where條件中沒有用到索引,但是要取出的列title是索引包含的列,所以只要全表掃描索引即可,直接使用索引樹查找數據)

mysql> explain select title from film\G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: film

         type: index

possible_keys: NULL

          key: idx_title

      key_len: 767

          ref: NULL

         rows: 1024

        Extra: Using index

1 row in set (0.00 sec)

 

type = range ,索引範圍掃描,常見於<、<=、>、>=、between等操作符(因爲customer_id是索引,所以只要查找索引的某個範圍即可,通過索引找到具體的數據)

mysql> explain select * from payment where customer_id > 300 and customer_id < 350\G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: payment

         type: range

possible_keys: idx_fk_customer_id

          key: idx_fk_customer_id

      key_len: 2

          ref: NULL

         rows: 1294

        Extra: Using where

1 row in set (0.01 sec)

 

type = ref ,使用非唯一性索引或者唯一索引的前綴掃描,返回匹配某個單獨值的記錄行。

(1)使用非唯一性索引customer_id單表查詢

mysql> explain select * from payment where customer_id = 350\G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: payment

         type: ref

possible_keys: idx_fk_customer_id

          key: idx_fk_customer_id

      key_len: 2

          ref: const

         rows: 23

        Extra:

1 row in set (0.00 sec)

(2)使用非唯一性索引聯表查詢(由於customer_id在a表中不是主鍵,是普通索引(非唯一),所以是ref)

mysql> explain select b.*, a.* from payment a ,customer b where a.customer_id = b.customer_id\G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: b

         type: ALL

possible_keys: PRIMARY

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 541

        Extra:

*************************** 2. row ***************************

           id: 1

  select_type: SIMPLE

        table: a

         type: ref

possible_keys: idx_fk_customer_id

          key: idx_fk_customer_id

      key_len: 2

          ref: sakila.b.customer_id

         rows: 14

        Extra:

2 rows in set (0.00 sec)  

 

type = eq_ref,相對於ref來說就是使用的是唯一索引,對於每個索引鍵值,只有唯一的一條匹配記錄(在聯表查詢中使用primary key或者unique key作爲關聯條件)

(在film和film_text中film_id都是主鍵,即都是唯一索引)

mysql> explain select * from film a ,film_text b where a.film_id = b.film_id\G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: b

         type: ALL

possible_keys: PRIMARY

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 1000

        Extra:

*************************** 2. row ***************************

           id: 1

  select_type: SIMPLE

        table: a

         type: eq_ref

possible_keys: PRIMARY

          key: PRIMARY

      key_len: 2

          ref: sakila.b.film_id

         rows: 1

        Extra: Using where

2 rows in set (0.00 sec)

 

type = const/system,單表中最多隻有一條匹配行,查詢起來非常迅速,所以這個匹配行中的其他列中的值可以被優化器在當前查詢中當做常量來處理。例如根據主鍵或者唯一索引進行的查詢。

mysql> explain select * from film  where film_id = 1\G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: film

         type: const

possible_keys: PRIMARY

          key: PRIMARY

      key_len: 2

          ref: const

         rows: 1

        Extra:

1 row in set (0.02 sec)

 

註釋:如果上表中film表中只有一行數據,那麼type就是system。

 

type = NULL,MYSQL不用訪問表或者索引就直接能到結果。

mysql> explain select 1 from dual  where 1\G (dual是一個虛擬的表,可以直接忽略)

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: NULL

         type: NULL

possible_keys: NULL

          key: NULL

      key_len: NULL

          ref: NULL

         rows: NULL

        Extra: No tables used

1 row in set (0.00 sec)

 

mysql> select 1+1 from dual;

+-----+

| 1+1 |

+-----+

|   2 |

+-----+

1 row in set (0.05 sec)

 

explain extended

mysql> explain extended select sum(amount) from customer a ,payment b where 1 = 1 and a.customer_id = b.customer_id and email = '[email protected]'\G

*************************** 1. row ***************************

           id: 1

  select_type: SIMPLE

        table: a

         type: ALL

possible_keys: PRIMARY

          key: NULL

      key_len: NULL

          ref: NULL

         rows: 541

     filtered: 100.00

        Extra: Using where

*************************** 2. row ***************************

           id: 1

  select_type: SIMPLE

        table: b

         type: ref

possible_keys: idx_fk_customer_id

          key: idx_fk_customer_id

      key_len: 2

          ref: sakila.a.customer_id

         rows: 14

     filtered: 100.00

        Extra: 

2 rows in set, 1 warning (0.00 sec)

 

mysql> show warnings\G

*************************** 1. row ***************************

  Level: Note

   Code: 1003

Message: select sum(`sakila`.`b`.`amount`) AS `sum(amount)` from `sakila`.`customer` `a` join `sakila`.`payment` `b` where ((`sakila`.`b`.`customer_id` = `sakila`.`a`.`customer_id`) and (`sakila`.`a`.`email` = '[email protected]'))

1 row in set (0.00 sec)

參考博客:https://blog.csdn.net/qq_27676247/article/details/79387637

 

SQL執行計劃詳解explain

參考:https://www.cnblogs.com/yhtboke/p/9467763.html

1.使用explain語句去查看分析結果 
如explain select * from test1 where id=1;會出現:id selecttype table type possible_keys key key_len ref rows extra各列。


其中, 
type=const表示通過索引一次就找到了; 
key=primary的話,表示使用了主鍵; 
type=all,表示爲全表掃描; 
key=null表示沒用到索引。

type=ref,因爲這時認爲是多個匹配行,在聯合查詢中,一般爲REF。

 

2.MYSQL中的組合索引 
假設表有id,key1,key2,key3,把三者形成一個組合索引,則如: 
複製代碼 代碼如下:

  1.  

    where key1=....

  2.  

    where key1=1 and key2=2

  3.  

    where key1=3 and key2=3 and key3=2

根據最左前綴原則,這些都是可以使用索引的,如from test where key1=1 order by key3,用explain分析的話,只用到了normal_key索引,但只對where子句起作用,而後面的order by需要排序。

 

3.使用慢查詢分析(實用)
在my.ini中: 
long_query_time=1 
log-slow-queries=d:\mysql5\logs\mysqlslow.log 
把超過1秒的記錄在慢查詢日誌中 
可以用mysqlsla來分析之。也可以在mysqlreport中,有如 
DMS分別分析了select ,update,insert,delete,replace等所佔的百分比


4.MYISAM和INNODB的鎖定 
myisam中,注意是表鎖來的,比如在多個UPDATE操作後,再SELECT時,會發現SELECT操作被鎖定了,必須等所有UPDATE操作完畢後,再能SELECT
innodb的話則不同了,用的是行鎖,不存在上面問題。 

 

5.MYSQL的事務配置項 
innodb_flush_log_at_trx_commit=1 
表示事務提交時立即把事務日誌flush寫入磁盤,同時數據和索引也更新,很費性能。
innodb_flush_log_at_trx_commit=0 
事務提交時,不立即把事務日誌寫入磁盤,每隔1秒寫一次,MySQL掛了可能會丟失事務的數據。
innodb_flush_log_at_trx_commit=2 ,在整個操作系統 掛了時纔可能丟數據,一般不會丟失超過1-2秒的更新。
事務提交時,立即寫入磁盤文件(這裏只是寫入到系統內核緩衝區,但不立即刷新到磁盤,而是每隔1秒刷新到磁盤,同時更新數據和索引),這種方案是不是性價比好一些,當然如何配置,決定於你對系統數據安全性的要求。

 

使用explain關鍵字可以模擬優化器執行SQL查詢語句,從而知道MySQL是如何處理你的SQL語句的,分析你的查詢語句或是表結構的性能瓶頸。

explain執行計劃包含的信息

這裏寫圖片描述

其中最重要的字段爲:id、type、key、rows、Extra

各字段詳解

id

select查詢的序列號,包含一組數字,表示查詢中執行select子句或操作表的順序 
三種情況: 
1、id相同:執行順序由上至下 
這裏寫圖片描述

2、id不同:如果是子查詢,id的序號會遞增,id值越大優先級越高,越先被執行 
這裏寫圖片描述

3、id相同又不同(兩種情況同時存在):id如果相同,可以認爲是一組,從上往下順序執行;在所有組中,id值越大,優先級越高,越先執行 
這裏寫圖片描述

select_type

查詢的類型,主要是用於區分普通查詢、聯合查詢、子查詢等複雜的查詢

1、SIMPLE:簡單的select查詢,查詢中不包含子查詢或者union 
2、PRIMARY:查詢中包含任何複雜的子部分,最外層查詢則被標記爲primary 
3、SUBQUERY:在select 或 where列表中包含了子查詢 
4、DERIVED:在from列表中包含的子查詢被標記爲derived(衍生),mysql或遞歸執行這些子查詢,把結果放在零時表裏 
5、UNION:若第二個select出現在union之後,則被標記爲union;若union包含在from子句的子查詢中,外層select將被標記爲derived 
6、UNION RESULT:從union表獲取結果的select 
這裏寫圖片描述

type

訪問類型,sql查詢優化中一個很重要的指標,結果值從好到壞依次是:

system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL

一般來說,好的sql查詢至少達到range級別,最好能達到ref

1、system:表只有一行記錄(等於系統表),這是const類型的特例,平時不會出現,可以忽略不計

2、const:表示通過索引一次就找到了,const用於比較primary key 或者 unique索引。因爲只需匹配一行數據,所有很快。如果將主鍵置於where列表中,mysql就能將該查詢轉換爲一個const 
這裏寫圖片描述

3、eq_ref:唯一性索引掃描,對於每個索引鍵,表中只有一條記錄與之匹配。常見於主鍵 或 唯一索引掃描。 
這裏寫圖片描述
注意:ALL全表掃描的表記錄最少的表如t1表

4、ref:非唯一性索引掃描,返回匹配某個單獨值的所有行。本質是也是一種索引訪問,它返回所有匹配某個單獨值的行,然而他可能會找到多個符合條件的行,所以它應該屬於查找和掃描的混合體 
這裏寫圖片描述

5、range:只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了那個索引。一般就是在where語句中出現了bettween、<、>、in等的查詢。這種索引列上的範圍掃描比全索引掃描要好。只需要開始於某個點,結束於另一個點,不用掃描全部索引 
這裏寫圖片描述

6、index:Full Index Scan,index與ALL區別爲index類型只遍歷索引樹。這通常爲ALL塊,應爲索引文件通常比數據文件小。(Index與ALL雖然都是讀全表,但index是從索引中讀取,而ALL是從硬盤讀取) 
這裏寫圖片描述

7、ALL:Full Table Scan,遍歷全表以找到匹配的行 
這裏寫圖片描述

possible_keys

查詢涉及到的字段上存在索引,則該索引將被列出,但不一定被查詢實際使用

key

實際使用的索引,如果爲NULL,則沒有使用索引。 
查詢中如果使用了覆蓋索引,則該索引僅出現在key列表中 
這裏寫圖片描述
這裏寫圖片描述

key_len

表示索引中使用的字節數,查詢中使用的索引的長度(最大可能長度),並非實際使用長度,理論上長度越短越好。key_len是根據表定義計算而得的,不是通過表內檢索出的

ref

顯示索引的那一列被使用了,如果可能,是一個常量const。

rows

根據表統計信息及索引選用情況,大致估算出找到所需的記錄所需要讀取的行數

Extra

不適合在其他字段中顯示,但是十分重要的額外信息

1、Using filesort : 
mysql對數據使用一個外部的索引排序,而不是按照表內的索引進行排序讀取。也就是說mysql無法利用索引完成的排序操作成爲“文件排序” 
這裏寫圖片描述
由於索引是先按email排序、再按address排序,所以查詢時如果直接按address排序,索引就不能滿足要求了,mysql內部必須再實現一次“文件排序”

2、Using temporary: 
使用臨時表保存中間結果,也就是說mysql在對查詢結果排序時使用了臨時表,常見於order by 和 group by 
這裏寫圖片描述

3、Using index: 
表示相應的select操作中使用了覆蓋索引(Covering Index),避免了訪問表的數據行,效率高 
如果同時出現Using where,表明索引被用來執行索引鍵值的查找(參考上圖) 
如果沒用同時出現Using where,表明索引用來讀取數據而非執行查找動作 
這裏寫圖片描述
覆蓋索引(Covering Index):也叫索引覆蓋。就是select列表中的字段,只用從索引中就能獲取,不必根據索引再次讀取數據文件,換句話說查詢列要被所建的索引覆蓋。 
注意: 
a、如需使用覆蓋索引,select列表中的字段只取出需要的列,不要使用select * 
b、如果將所有字段都建索引會導致索引文件過大,反而降低crud性能

4、Using where : 
使用了where過濾

5、Using join buffer : 
使用了鏈接緩存

6、Impossible WHERE: 
where子句的值總是false,不能用來獲取任何元祖 
這裏寫圖片描述

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

8、distinct: 
優化distinct操作,在找到第一個匹配的元祖後即停止找同樣值得動作

綜合Case

這裏寫圖片描述

執行順序 
1(id = 4)、【select id, name from t2】:select_type 爲union,說明id=4的select是union裏面的第二個select。

2(id = 3)、【select id, name from t1 where address = ‘11’】:因爲是在from語句中包含的子查詢所以被標記爲DERIVED(衍生),where address = ‘11’ 通過複合索引idx_name_email_address就能檢索到,所以type爲index。

3(id = 2)、【select id from t3】:因爲是在select中包含的子查詢所以被標記爲SUBQUERY。

4(id = 1)、【select d1.name, … d2 from … d1】:select_type爲PRIMARY表示該查詢爲最外層查詢,table列被標記爲 “derived3”表示查詢結果來自於一個衍生表(id = 3 的select結果)。

5(id = NULL)、【 … union … 】:代表從union的臨時表中讀取行的階段,table列的 “union 1, 4”表示用id=1 和 id=4 的select結果進行union操作。

發佈了40 篇原創文章 · 獲贊 9 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章