MySQL 學習四 SQL優化

 MySQL邏輯架構:mysql

  第一層:客戶端層,鏈接處理,受權認證,安全等功能。sql

    第二層:核心層,查詢解析,分析,優化,緩存,內置函數(時間,數學,加密),存儲過程,觸發器,視圖數據庫

    第三層:存儲引擎。負責MySQL中數據的存儲和提取。緩存

  

MySQL查詢過程安全

  • 客戶端/服務端通訊協議:須要注意的是,若是查詢實在是太大,服務端會拒絕接收更多數據並拋出異常,於是在實際開發中,儘可能保持查詢簡單且只返回必需的數據,減少通訊間數據包的大小和數量是一個很是好的習慣,這也是查詢中儘可能避免使用 SELECT * 以及加上 LIMIT 限制的緣由之一。
  • 查詢緩存: 緩存命中的狀況下,查詢不會被解析,不會生成執行計劃,更不會被執行。因此兩個查詢在任何字符上的不一樣(例如:空格、註釋),都會致使緩存不會命中。若是查詢中包含任何用戶自定義函數、存儲函數、用戶變量、臨時表、MySQL 庫中的系統表,其查詢結果都不會被緩存。查詢緩存什麼時候失效呢?MySQL 的查詢緩存系統會跟蹤查詢中涉及的每一個表,若是這些表(數據或結構)發生變化,那麼和這張表相關的全部緩存數據都將失效。正由於如此,在任何的寫操做時,MySQL 必須將對應表的全部緩存都設置爲失效。

    緩存對系統的額外消耗也不只僅在寫操做,讀操做也不例外:性能優化

    • 任何的查詢語句在開始以前都必須通過檢查,即便這條 SQL 語句永遠不會命中緩存。服務器

    • 若是查詢結果能夠被緩存,那麼執行完成後,會將結果存入緩存,也會帶來額外的系統消耗。架構

      基於此,咱們要知道並非什麼狀況下查詢緩存都會提升系統性能,緩存和失效都會帶來額外消耗,只有當緩存帶來的資源節約大於其本            身消耗的資源時,纔會給系統帶來性能提高。函數

 

    最後的忠告是不要輕易打開查詢緩存,特別是寫密集型應用。若是你實在是忍不住,能夠將 query_cache_type 設置爲 DEMAND。工具

 

    這時只有加入 SQL_CACHE 的查詢纔會走緩存,其餘查詢則不會,這樣能夠很是自由地控制哪些查詢須要被緩存。

 

  • 語法解析和預處理 

   解析語法,生成解析樹。進行合法校驗。

  • 查詢優化:    SQL解析,預處理,優化生成執行計劃。
  • 查詢執行引擎: 生成執行計劃,並執行查詢。
  • 返回結果給客戶端:同時緩存結果。

 (不要聽信你看到的關於優化的「絕對真理」,包括本文所討論的內容,而應該是在實際的業務場景下經過測試來驗證你關於執行計劃以及響應時間的假設。

 

1 學習使用EXPLAIN 

2 建立正確的索引

  數據庫的索引像書的索引同樣,他們的位置信息被保存,而且包含數據庫的主要信息。可使用EXPLAIN來查找

缺失的索引。

3 拒絕默認的設置:有三個關於MySQL性能優化的設置:

  innodb_buffer_pool_size:數據和索引被用做緩存的緩衝池。當數據庫服務器有大量的系統內存時,能夠用。

                                                這個設置不要過大,也不要頻繁的引發交換。      

  innodb_log_file_size:單個InnoDB日誌文件大小。

  max_connections:最大鏈接數

4 將數據庫載入內存

  將頻繁訪問的數據放入內存(好比30%的數據放入內存)

5 SSD存儲

6 橫向擴展??

  縱向擴展

  橫向擴展

7 追求可視化

  數據庫受到流量負荷的影響,應用程序等致使的錯誤,爲了快速、有效的解決問題,須要有監控機制。

  經常使用的監測工具:    MySQL企業監控器 /  Monyog /    Percona

8 Scheme設計與數據類型優化

  選擇數據類型只要遵循小而簡單的原則就好,越小的數據類型一般會更快,佔用更少的磁盤、內存,處理時須要的 CPU 週期也更少。好比,整型就比字符操做代價低,於是會使用整型來存儲 ip 地址,使用 DATETIME 來存儲時間,而不是使用字符串。 

  • 在列上建立索引的話,應該把NULL改成NOT NULL: 不然索引的效率會大打折扣。
  • 對整數類型指定寬度,沒有任何做用
  • UNSIGNED表示不容許負值:
  • 沒有太大必要使用DECIMAL數據類型。就算在須要存儲財務數據時,任然可使用BIGINT。好比精確到萬分之一,那麼能夠將數據乘以一百萬後使用BIGINT存儲。這樣能夠避免浮點數計算不許確和DECIMAL精確計算代價高的問題。
  • TIMESTAMP使用4個字節存儲,DATETIME使用8個字節存儲。可是TIMESTAMP只能表示1970-2038年,而且TIMESTAMP的值因時區不一樣而不一樣  
  • 大多數狀況下,沒有必要使用枚舉類型,缺點是:枚舉的字符串列表是固定的,添加和刪除必須使用ALTER TABLE
  • schema的列不要太多。
  • 大表ALTER TABLE很是耗時。

 9 建立高性能索引

  索引:一般說的索引時B-Tree索引。InnoDB用的是B+Tree.

  平衡二叉樹: 若是想二叉樹的查詢性能高,須要二叉樹是平衡二叉樹。

  爲何MYSQL不用平衡二叉樹,而是用B+Tree樹?隨着數據庫中數據的增長,索引自己大小隨之增長,不可能所有存儲在內存中,所以索引每每以索引文件的形式存儲在磁盤上。這樣的話,索引查找過程當中就要產生磁盤 I/O 消耗,相對於內存存取,I/O 存取的消耗要高几個數量級。能夠想象一下一棵幾百萬節點的二叉樹的深度是多少?若是將這麼大深度的一顆二叉樹放磁盤上,每讀取一個節點,須要一次磁盤的 I/O 讀取,整個查找的耗時顯然是不可以接受的。那麼如何減小查找過程當中的 I/O 存取次數?一種行之有效的解決方法是減小樹的深度,將二叉樹變爲 m 叉樹(多路搜索樹),而 B+Tree 就是一種多路搜索樹

   B+Tree樹特徵:

  • 全部關鍵字都存在葉子節點:   每一個節點設置爲頁的整數倍(預讀取時,能夠讀一頁)
  •  葉子節點由指針鏈接:這樣便於區間查找。

10 MySQL不使用索引的狀況:非獨立的列

   「獨立的列」 :索引列不能是表達式的一部分,也不能是函數的參數

11 前綴索引

      列很長的狀況下,索引開始的部分字符,有效節約索引空間(須要前面的部分有必定的區分度)

12 多列索引和索引順序:

       在多數狀況下,在多個列上創建獨立的索引並不能提升查詢性能,理由很是簡單,MySQL 不知道選擇哪一個索引的查詢效率更好(由於數據庫中有多個索引的B+Tree, MySQL只能選擇一個樹作索引)。

  多個列之間用AND時:聯合索引優於獨立索引。

  多個列之間用OR、uniton時:每每會不走索引。

  這種狀況下,創建包含多個列的聯合索引更加高效

       建立聯合索引時, 把選擇性更高的放在前面(由於這樣,經過第一個過濾條件就能過濾掉大讀書數據)。

13 避免多個範圍條件

     只能用其中一個索引。

14 覆蓋索引

  若是一個索引包含或者說覆蓋全部須要查詢的字段的值(就是select 後面的列),那麼就沒有必要再回表查詢,這就稱爲覆蓋索引。

15 使用索引掃描來排序

  • 對結果集進行排序的操做:
  • 按照索引順序掃描得出的結果天然是有序的

       掃描索引自己很快,由於只須要從一條索引記錄移動到相鄰的下一條記錄。但若是索引自己不能覆蓋全部須要查詢的列,那麼就不得不每掃描一條索引記錄就回表查詢一次對應的行。

  在設計索引時,若是一個索引既可以知足排序,又知足查詢,是最好的!!只有當索引的列順序和 ORDER BY 子句的順序徹底一致,而且全部列的排序方向也同樣時,纔可以使用索引來對結果作排序。

  若是查詢須要關聯多張表,則只有 ORDER BY 子句引用的字段所有爲第一張表時,才能使用索引作排序。

       ORDER BY 子句和查詢的限制是同樣的,都要知足最左前綴的要求。

16 避免冗餘和重複索引

17 刪除長期未使用的索引

18 查詢優化

  • count:兩個做用,其一是統計某個列值的數量,其二是統計行數。統計列值時,要求列值是非空的,它不會統計 NULL。若是確認括號中的表達式不可能爲空時,實際上就是在統計行數。若是要統計行數,直接使用 COUNT(*),意義清晰,且性能更好。一般來講,執行 COUNT() 都須要掃描大量的行才能獲取到精確的數據,所以很難優化。
  • 優化關聯查詢:表之間經過冗餘字段關聯,比使用Join有更好的性能。             

          在關聯查詢的狀況下:Group By中的表達式只涉及到一個表中的列。這樣纔有可能使用索引優化。

              A和B表用c類關聯時,不須要在A上創建索引,在B上建索引就OK(由於,A表不管是否有索引,都要遍歷,B表和C表則須要走索引去尋找匹配的記錄)。

19 優化LIMIT分頁

LIMIT 10000 20 這樣的查詢,MySQL 須要查詢 10020 條記錄而後只返回 20 條記錄,前面的 10000 條都將被拋棄,這樣的代價很是高。

優化這種查詢一個最簡單的辦法就是儘量的使用覆蓋索引掃描,而不是查詢全部的列

20 優化UNION

  除非確實須要服務器去重,不然就必定要使用 UNION ALL,若是沒有 ALL 關鍵字,MySQL 會給臨時表加上 DISTINCT 選項,這會致使整個臨時表的數據作惟一性檢查,這樣作的代價很是高。

21 假設有聯合索引  (user_name, sex, age),  如下三個查詢,其實謂詞的順序不重要,都會用到聯合索引的,這是由於MySQL作了優化。

  可是聯合索引的順序卻很重要,看下面22和23

select * from test where user_name=’test1’ and sex>0 and age =10
select * from test where sex>0 and user_name=’test1’ and age =10
select * from test where age =10 and user_name='test1' and sex>0

 22 最左原則

  mysql創建多列索引(聯合索引)有最左前綴的原則,即最左優先,如:

  若是有一個2列的索引(col1,col2),則已經對(col1)、(col1,col2)上創建了索引;
  若是有一個3列索引(col1,col2,col3),則已經對(col1)、(col1,col2)、(col1,col2,col3)上創建了索引;

23  聯合索引總結

  • "一個頂三個"。建了一個(a,b,c)的複合索引,那麼實際等於建了(a),(a,b),(a,b,c)三個索引,由於每多一個索引,都會增長寫操做的開銷和磁盤空間的開銷。對於大量數據的表,這但是不小的開銷!
  • 覆蓋索引索引能覆蓋全部要查詢的列,不用回表)。一樣的有複合索引(a,b,c),若是有以下的sql: select a,b,c from table where a=1 and b = 1。那麼MySQL能夠直接經過遍歷索引取得數據,而無需回表,這減小了不少的隨機io操做。減小io操做,特別的隨機io實際上是dba主要的優化策略。因此,在真正的實際應用中,覆蓋索引是主要的提高性能的優化手段之一

          (一直在思考,對於select 後面的字段是否須要創建索引, 對於上面的a,b,c索引,不須要回表;對於a,b索引,還須要回表。)

  • 引列越多,經過索引篩選出的數據越少。有1000W條數據的表,有以下sql:select * from table where a = 1 and b =2 and c = 3,假設假設每一個條件能夠篩選出10%的數據,若是隻有單值索引,那麼經過該索引能篩選出1000W*10%=100w 條數據,而後再回表從100w條數據中找到符合b=2 and c= 3的數據,而後再排序,再分頁;若是是複合索引,經過索引篩選出1000w *10% *10% *10%=1w,而後再排序、分頁,哪一個更高效,一眼便知 !!!!!!!!!

24 like

  %xx   --   不走索引;    XX% -- 走索引;

25 OR

  OR操做致使,不容易優化。

26  避免 select *

27 知道什麼時候使用臨時表

  防止對大表查詢兩次。還可使用臨時表,大幅減小鏈接大表所需的處理能力。

  若是你必須將一個錶鏈接到大表,該大表上又有條件只需將大表中所需的那部分數據提取到臨時表中,而後再與該臨時錶鏈接,就能夠提高查詢性能(這個在實際項目中用到過,在表join以前,先把大表過濾掉儘量多的行)

28 預暫存數據

  若是你有一個報表或存儲過程(或一組)要對大表執行相似的鏈接操做,經過提早鏈接表,並將它們持久化存儲到一個表中來預暫存數據,就能夠對你大有幫助。

 29 批量刪除和更新

30 避免嵌套視圖

31 不要使用逆向搜索

  SELECT * FROMCustomers WHERE RegionID <> 3 

       索引與該查詢結合使用,由於它是逆向搜索,須要藉助表掃描來逐行比較。

       優化方法:SELECT * FROM Customers WHERE RegionID<3 UNION ALL SELECT * FROM Customers WHERE RegionID >3

32 索引的使用原則

  • 避免對索引字段進行計算操做
  • 避免在索引字段上使用not,  <>,  != 
  • 避免在索引列上使用IS NULL 和 IS NOT  NULL
  • 避免在索引列上出現數據類型的轉換
  • 避免在索引字段上使用函數(儘可能在應用程序中實現)
  • 避免在索引的列中使用空值。

33 使用UNION all,  儘可能避免UNION(Union須要排序,而後去重

34 避免對索引列加函數修飾

where trunc(create_date)=trunc(:date1)  
原本create_date建索引了,可是加上trunc後,索引失效,改爲以下:
where create_date>=trunc(:date1) and create_date<trunc(:date1)+1< pre="">
where create_date between trunc(:date1) and trunc(:date1)+1-1/(24*60*60)

35 where子句中使用in, not in, or having.
使用exist , not exist代替in, not in

這個須要實踐的檢驗,待續
36 排序
  帶有DISTINCT,UNION,MINUS,INTERSECT,ORDER BY的SQL語句會啓動SQL引擎 執行,
耗費資源的排序(SORT)功能。 DISTINCT須要一次排序,其餘的至少兩次排序。
   這個深有感觸,MySQL的order by效率及其低下。

 37  應儘可能避開where子句中進行null值判斷:

   select id from t where num is null 能夠在num上設置默認值0,確保表中num列沒有null值,而後這樣查詢: select id from t                 where num=0

38 並非全部索引對查詢都有效:

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

39 索引並非越多越好,索引當然能夠提升相應的 select 的效率,但同時也下降了 insert 及 update 的效率

40  應儘量的避免更新索引數據列,由於索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將致使整個表記錄的順序的調整,會耗費至關大的資源。若應用系統須要頻繁更新索引數據列,那麼須要考慮是否應將該索引建爲索引。  

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

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

43 應儘可能避免在 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  

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

45 不少時候用 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)

46  儘可能使用 TINYINT、 SMALLINT、 MEDIUM_INT做爲整數類型而非 INT,若是非負則加上 UNSIGNED

47 VARCHAR的長度只分配真正須要的空間

 48 使用枚舉或整數代替字符串類型

49OR改寫成 IN: OR的效率是n級別, IN的效率是log(n)級別,in的個數建議控制在200之內

相關文章
相關標籤/搜索