作後臺開發的程序猿一般須要寫各類各樣的sql,可不少時候寫出來的sql雖然能知足功能性需求,性能上卻不盡人意。若是業務複雜,表結構和索引設計又不合理的話,寫出來的sql執行時間可能會達到幾十甚至上百秒,對於生產環境來講,這是至關恐怖的一件事。所以,瞭解一些常見的mysql優化技巧頗有必要。本文將從表結構和索引設計,sql執行原理,sql編寫優化3方面進行分析和講解,但願能對你們有所幫助。mysql
一、表結構,字段設計是否合理?sql
這是最基礎也是最容易忽視的一個環節。良好的表結構設計是sql優化的基礎,在這個存儲廉價,空間足夠的時代,設計表的過程當中,不必定要徹底知足範式理論,咱們能夠經過適當的冗餘設計,避免連表查詢,達到以空間來換取時間的目的。設計表的時候,咱們會根據業務需求來決定建幾個表,表之間經過哪些外鍵來關聯。並且一般須要考慮到數據規模(單表記錄數最好不要超過千萬,若是超過可能須要分表分區,包括垂直分表和水平分表)、查詢更新頻率(哪些字段常常用於查詢,哪些常常用於更新),各字段的類型和長度取值,在哪些字段上建哪一種類型的索引等等。數據庫
比方說,若是你是innodb存儲引擎,那麼你的主鍵最好設計成自增的,這樣效率最高。由於innodb存儲引擎的索引是基於B+樹實現,若是採用自增設計,就能快速找到插入節點的位置進行插入或刪除,對其餘節點影響較小,避免頻繁分裂樹結構。有的公司設計表的時候喜歡採用UUID的方式來做爲主鍵,這樣的好處是數據遷移的時候,主鍵不會變,能找到對應關係,可是會有2個問題:一、UUID的長度是36位,佔用字節較長,尤爲對於innoDB來講,創建輔助索引的時候,輔助索引裏存儲的都是主鍵的值,這會致使輔助索引佔據空間變大。二、UUID是無序的,每次插入或者刪除一條記錄的時候,爲了維持索引的特性,可能會致使節點頻繁分裂,這樣很是影響效率。網絡
在設計字段的時候,儘可能採用整形的,好比用tinyint 代替char(1),這樣便於存儲和計算。在知足業務的前提下,長度越短越好,若是有大對象,好比text或blob類型的字段,而且這些字段查詢頻率較低時,能夠考慮拆表來單獨存儲(也就是垂直分表),避免對主表形成影響。此外,設計表的時候,最好設計爲not null,由於容許爲null時,mysql還須要有個字節來標識是不是null,並且mysql索引沒法存儲null,若是在一列容許null 的索引中使用where colum is null,那麼mysql是不會走索引的。那若是有的字段就是沒值怎麼辦?能夠用空字符串或者0這些代替。mysql優化
二、sql執行原理性能
寫好了sql後,sql是怎麼執行的呢?當咱們運行sql的時候,會經歷客戶端發送請求,服務端接受請求並解析sql,生成sql執行計劃,執行並將結果返回給客戶端這些過程。要優化sql,首先要知道sql到底在哪些環節花了多長時間。這裏不去分析網絡因素對sql形成的影響,咱們只需關注sql生成的執行計劃,這個執行計劃能很大程度上幫助咱們找到優化sql的方向。那怎麼看sql的執行計劃呢?explain 你的sql。好比在mysql 5.6自帶的sakila數據庫上執行以下sql:mysql索引
能夠看到有id,select_type,partitions,type,possible_keys等等內容。首先說一下,比較重要的有id,select_type,type(至關重要),key(至關重要),key_len(可能重要),extra(至關重要)這幾列。其餘的列就不介紹了。這些內容都表明什麼意義呢?優化
id一般表示執行順序,好比有3行,id分別爲1,1,2,那麼執行順序就是1,1,2,一般id的個數對應select的個數。編碼
select_type表示查詢類型,主要有如下幾種:spa
SIMPLE:簡單SELECT(不使用UNION或子查詢等)
PRIMARY:最外面的SELECT
UNION:UNION中的第二個或後面的SELECT語句
DEPENDENT UNION:UNION中的第二個或後面的SELECT語句,取決於外面的查詢
UNION RESULT:UNION的結果。
SUBQUERY:子查詢中的第一個SELECT
DEPENDENT SUBQUERY:子查詢中的第一個SELECT,取決於外面的查詢
DERIVED:導出表的SELECT(FROM子句的子查詢)
type:表示使用了哪一種類別的鏈接,有無使用索引,是使用Explain命令分析性能瓶頸的關鍵項之一,性能由好到壞依次爲:system > const > eq_ref > ref > fulltext > ref_or_null > index_merge > unique_subquery > index_subquery > range > index > ALL。通常來講,得保證查詢至少達到range級別,最好能達到ref,不然就可能會出現性能問題。
key:表示使用的索引,若是沒有選擇索引,則爲NULL。
key_len:表示索引長度,對於單列索引,該值意義不大,對於聯合索引,則有重要做用,key_len的大小顯示了聯合索引中真正用到的哪幾列,若是是聯合索引,則該值越大表示走的索引列越多,查詢效率越高,這裏涉及到索引前綴的知識,該部分後面有空再講。對於該列的值,也有計算公式:若是是單列索引,則key_len=索引列的長度*字符編碼佔用的字節數(UTF8編碼爲3字節,GBK爲2字節,latin爲1字節)+標識是否容許null的字節數(1字節)+內容長度(針對可變長列,1字節),舉個例子:
該表中,city_id是主鍵,city字段是varchar類型,長度爲50,默認爲null,執行explain select city from sakila.city,以下:
能夠發現,這裏走了覆蓋索引,順便提下,覆蓋索引就是sql的查詢內容經過走sql索引就能查到,這種狀況就是覆蓋索引,因此這裏咱們看到,即便咱們不加where條件也能走索引。索引列是city_name,key_len爲152,怎麼來的呢?對照上面的公式:50長度*3(UTF8編碼一個字符3個字節)+1(標識是否爲null)+1(標識內容的長度),這樣是否是很清晰了?
最後這列Extra:包含MySQL解決查詢的詳細信息,也是關鍵參考項之一。當這列出現了Using filesort(出現這種狀況九死一輩子,頗有必要優化)和Using temporary(這裏就是十死0生了,必須優化!)就須要格外注意了。
三、優化你的sql
當完成了上面2步之後,若是發現你的sql很慢,這時候就必須對咱們的sql進行優化了。2個大的思路是先問問本身:是否建了索引?索引建的是否合適?當咱們分析一條sql慢的時候,咱們須要考慮,這條sql查詢的內容是否建了索引呢?若是沒有,那要在哪列建哪一種索引呢?好比咱們要從用戶表(>100W條記錄)中根據姓名查某個用戶,若是沒有建索引,顯然會很慢,那麼怎麼建索引呢?你可能會說很簡單嘛,就在姓名上建個索引不就完了嘛。那假如(只是假如)姓名這列裏,100W個用戶中,有50W個叫張三的,20W個叫李四的,30W個王五的,你在這裏建合適嗎?顯然不合適,或者說,僅僅對這列建單列索引不合適,由於選擇性太差。並且這會致使個問題,當sql存儲引擎發現走全表掃描比走索引更快的時候,它會放棄走索引,直接掃表。這裏有個最重要的關鍵詞:選擇性,選擇性能夠理解爲:該表中該列的不重複數/總記錄數,該比值在0-1之間,越接近1說明選擇性越好,惟一索引的選擇性就是1,所以惟一索引是性能最好的索引。像上面用戶表中,該表的選擇性咱們能夠這麼查:select count(distinct name)/count(*) from customer;所以咱們要作的,就是想辦法提升索引的選擇性,能夠採用建聯合索引,或者部分索引(就是取該列的N個字符來建索引,可是這種索引不能用於group by中)等等,遵循這個思路,咱們就明白,有的開發員在性別列建索引,其實並非一個好選擇,由於選擇性太差。要建高效的索引,就必定是選擇性好的索引。
端午假期第一天,上午看了會世界盃,下午閒的無聊寫了這篇博客,歡迎拍磚交流,轉載請務必註明出處,謝謝。