當前位置:
首頁 > 知識 > MySQL 性能優化 : 索引和查詢優化

MySQL 性能優化 : 索引和查詢優化

(點擊

上方公眾號

,可快速關注)




來源:秋日芒草 ,


my.oschina.net/qrmc/blog/1822373




要知道為什麼使用索引,要知道如何去使用好索引,使自己的查詢達到最優性能,需要先了解索引的數據結構和磁碟的存取原理




參考博客:

MySQL索引背後的數據結構及演算法原理





http://blog.codinglabs.org/articles/theory-of-mysql-index.html




如上這篇博客寫的挺好,我就不再造輪子了,對如上博客,我總結如下幾個重要的要點:




1. 不使用順序查找,因為順序查找比較慢,通過特定數據結構的特點來提升查詢速度,這種數據結構就是可以理解成索引。






2. 索引一般以文件形式存儲在磁碟上,索引檢索需要磁碟I/O操作,為了盡量減少磁碟I/O。磁碟往往不是嚴格按需讀取,而是每次都會預讀,而且主存和磁碟以頁為單位交換數據,所以在讀取的數據不在主存中時,會從磁碟中讀取一批數據(頁)到主存中。







3. 不管在哪種程序優化上,要想快速挺高性能,直接將常用的、少變更的數據直接讀取到內存中,使用的時候就直接在內存上讀取,而不去磁碟上讀取,減少I/O操作,這樣就能使程序快上10倍以上。但由於內存容量的限制,也不可能將所有的數據都放內存中。






MySQL索引分類




普通索引

:最基本的索引,沒有任何限制。


唯一索引

:與」普通索引」類似,不同的就是:索引列的值必須唯一,但允許有空值。


主鍵索引

:它是一種特殊的唯一索引,不允許有空值。

全文索引

:僅可用於 MyISAM 表,針對較大的數據,生成全文索引很耗時好空間。


組合索引

:為了更多的提高mysql效率可建立組合索引,遵循」最左前綴「原則。




覆蓋索引

(Covering Indexes)就是直接走的索引,直接在內存中就拿到值,不需要查詢資料庫。如分頁就要走覆蓋索引,因為性能比較高。




聚簇索引

(Clustered Indexes),主鍵就是聚集索引。聚簇索引保證關鍵字的值相近的元組存儲的物理位置也相同(所以字元串類型不宜建立聚簇索引,特別是隨機字元串,會使得系統進行大量的移動操作),且一個表只能有一個聚簇索引。因為由存儲引擎實現索引,所以,並不是所有的引擎都支持聚簇索引。目前,只有solidDB和InnoDB支持。



非聚簇索引

:二級索引葉子節點保存的不是指行的物理位置的指針,而是行的主鍵值。這意味著通過二級索引查找行。InnoDB對主鍵建立聚簇索引。如果你不指定主鍵,InnoDB會用一個具有唯一且非空值的索引來代替。如果不存在這樣的索引,InnoDB會定義一個隱藏的主鍵,然後對其建立聚簇索引。一般來說,DBMS都會以聚簇索引的形式來存儲實際的數據,它是其它二級索引的基礎。




優化要注意的一些事(重點)




1. 索引其實就是一種歸類方式,當某一個欄位屬性都不能歸類,建立索引後是沒什麼效果的,或歸類就二種(0和1),且各自都數據對半分,建立索引後的效果也不怎麼強。



2. 主鍵的索引是不一樣的,要區別理解。




3. 當時間存儲為時間戳保存的可以建立前綴索引。




4. 在什麼是欄位上建立索引,需要根據查詢條件而定,不要一上來就建立索引,浪費內存還有可能用不到。




5. 大欄位(blob)不要建立索引,查詢也不會走索引。




6. 常用建立索引的地方:




  • 主鍵的聚集索引



  • 外鍵索引



  • 類別只有0和1就不要建索引了,沒有意義,對性能沒有提升,還影響寫入性能



  • 用模糊其實是可以走前綴索引




7. 唯一索引一定要小心使用,它帶有唯一約束,由於前期需求不明等情況下,可能造成我們對於唯一列的誤判。




8. 由於我們建立索引並想讓索引能達到最高性能,這個時候我們應當充分考慮該列是否適合建立索引,可以根據列的區分度來判斷,區分度太低的情況下可以不考慮建立索引,區分度越高效率越高。





SELECT COUNT(DISTINCT 列_xx)/COUNT(*) FROM 表




9. 寫入比較頻繁的時候,不能開啟MySQL的查詢緩存,因為在每一次寫入的時候不光要寫入磁碟還的更新緩存中的數據。




10. 建索引的目的:




1)加快查詢速度,使用索引後查詢有跡可循。


2)減少I/O操作,通過索引的路徑來檢索數據,不是在磁碟中隨機檢索。


3)消除磁碟排序,索引是排序的,走完索引就排序完成。




11. 其實建索引的原理就是將磁碟I/O操作的最小化,不在磁碟中排序,而是在內存中排好序,通過排序的規則去指定磁碟讀取就行,也不需要在磁碟上隨機讀取。




12. 由於磁碟整理磁碟碎片,所有有的時候我們也可以通過建立聚集索引來減少這一類的問題。




13. 當一個表中有100萬數據,而經常用到的數據只有40萬或40萬以下,是不用考慮建立索引的,沒什麼性能提升。




14. 什麼時候不適合建立索引:




1)頻繁更新的欄位不適合建立索引


2)where條件中用不到的欄位不適合建立索引,都用不到建立索引沒有意義還浪費空間


3)表數據可以確定比較少的不需要建索引


4)數據重複且發布比較均勻的的欄位不適合建索引(唯一性太差的欄位不適合建立索引),例如性別,真假值


5)參與列計算的列不適合建索引,如:





select * from table where amount+100>1000,-- 這樣是不走索引的,可以改造為:select * from table where amount>1000-100。




15. 使用count統計數據量的時候建議使用count(*)而不是count(列),因為count(*)MySQL是做了優化的。




16. 二次SQL查詢區別不大的時候,不能按照二次執行的時間來判斷優化結果,沒準第一次查詢後又保存緩存數據,導致第二次查詢速度比第二次快,很多時候我們看到的都是假象。




17. 什麼時候開MySQL的查詢緩存,交易系統(寫多、讀少)、SQL優化測試,建議關閉查詢緩存,論壇文章類系統(寫少、讀多),建議開啟查詢緩存。




18. Explain 執行計劃只能解釋SELECT操作。




19. 查詢優化可以考慮讓查詢走索引,走索引能提升查詢速度,索引覆蓋是最快的,如下就是讓分頁走覆蓋索引提高查詢速度。





Select * from fentrust e


Inner join (select fid from fentrust limit 4100000, 10) a on a.fid = e.fid






20. 子查詢比join快,雖然規律不絕對,但對大表多數有效




21. 複雜SQL語句優化的思路:




1)首先考慮在一個表中能不能取到有關的信息,盡量少關聯表


2)關聯條件爭取都走主鍵或外鍵查詢條件,能走到對應的索引


3)爭取在滿足業務上走小集合數據查找


4)INNER JOIN 和子查詢哪個更快,場景不一致速度也不同




22. where條件多條件一定要按照小結果集排大結果集前面




23. 盡量避免大事務操作,提高系統並發能力,有時無法避免,改用定時器延遲處理。




24. 什麼情況不走索引:





SELECT ` famount ` FROM ` fentrust ` WHERE ` famount `+10=30;-- 不會使用索引,因為所有索引列參與了計算 


SELECT `famount` FROM `fentrust` WHERE LEFT(`fcreateTime`,4) <1990; -- 不會使用索引,因為使用了函數運算,原理與上面相同 


SELECT * FROM ` fuser` WHERE `floginname` LIKE『138%" -- 走索引 


SELECT * FROM ` fuser ` WHERE ` floginname ` LIKE "%7488%" -- 不走索引 -- 正則表達式不使用索引,這應該很好理解,所以為什麼在SQL中很難看到regexp關鍵字的原因 -- 字元串與數字比較不使用索引; 


EXPLAIN SELECT * FROM `a` WHERE `a`=1 -- 不走索引 


select * from fuser where floginname="xxx" or femail="xx" or fstatus=1 --如果條件中有or,即使其中有條件帶索引也不會使用。換言之,就是要求使用的所有欄位,都必須建立索引, 我們建議大家盡量避免使用or 關鍵字




25. 如果MySQL估計使用全表掃描要比使用索引快,則不使用索引。




26. 使用UNION ALL 替換OR多條件查詢並集。




27. 在大數據表刪除也是一個問題,避免刪除過程資料庫奔潰,可以考慮分配刪除,一次刪1000條,刪完後等一會繼續刪除





delete from logs where log_date <= 』2012-11-01』 limit 1000




28. 大數據表優化:




1)建立匯總表


2)建立流水表


3)分庫分表




29. 建立匯總表,首先不用考慮分庫分表,使用定時器定時去匯總。




30. 分表,可以按水平或垂直切分。垂直分表其實就是將經常使用的數據和很少使用的數據進行垂直的切分,切分到不同的庫,提高單庫的數據容量,如:前3個月之前的交易記錄就可以放另一個庫中。




31. 建立流水表,數據冗餘,有這個表記錄流水變更就不用去寫複雜SQL計算流水。




32. 分庫,多資料庫相同庫結構,分發處理並發能力,但同時帶來了數據同步問題,也可以使用分庫做主備分離




32. SQL優化順序:




1)盡量少作計算。


2)盡量少 join。


3)盡量少排序。


4)盡量避免 select *。


5)盡量用 join 代替子查詢。


6)盡量少 or。


7)盡量用 union all 代替 union。


8)盡量早過濾。


9)避免類型轉換。


10)優先優化高並發的 SQL,而不是執行頻率低某些「大」SQL。


11)從全局出發優化,而不是片面調整。


12)儘可能對每一條運行在資料庫中的SQL進行 Explain。




33. 如下是30條大數據表優化要點:




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




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)盡量避免大事務操作,提高系統並發能力。




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




【關於投稿】




如果大家有原創好文投稿,請直接給公號發送留言。




① 留言格式:


【投稿】+《 文章標題》+ 文章鏈接

② 示例:


【投稿】《不要自稱是程序員,我十多年的 IT 職場總結》:http://blog.jobbole.com/94148/

③ 最後請附上您的個人簡介哈~






看完本文有收穫?請轉發分享給更多人


關注「ImportNew」,提升Java技能


喜歡這篇文章嗎?立刻分享出去讓更多人知道吧!

本站內容充實豐富,博大精深,小編精選每日熱門資訊,隨時更新,點擊「搶先收到最新資訊」瀏覽吧!


請您繼續閱讀更多來自 ImportNew 的精彩文章:

TAG:ImportNew |