程序員收藏必看系列:深度解析MySQL優化(二)

程序員收藏必看系列:深度解析MySQL優化(一)javascript

性能優化建議

下面會從3個不一樣方面給出一些優化建議。但請等等,還有一句忠告要先送給你:不要聽信你看到的關於優化的「絕對真理」,包括本文所討論的內容,而應該是在實際的業務場景下經過測試來驗證你關於執行計劃以及響應時間的假設。css

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

這裏總結幾個可能容易理解錯誤的技巧:html

  1. 一般來講把可爲NULL的列改成NOT NULL不會對性能提高有多少幫助,只是若是計劃在列上建立索引,就應該將該列設置爲NOT NULL。
  2. 對整數類型指定寬度,好比INT(11),沒有任何卵用。INT使用32位(4個字節)存儲空間,那麼它的表示範圍已經肯定,因此INT(1)和INT(20)對於存儲和計算是相同的。
  3. UNSIGNED表示不容許負值,大體可使正數的上限提升一倍。好比TINYINT存儲範圍是-128 ~ 127,而UNSIGNED TINYINT存儲的範圍倒是0 - 255。
  4. 一般來說,沒有太大的必要使用DECIMAL數據類型。即便是在須要存儲財務數據時,仍然可使用BIGINT。好比須要精確到萬分之一,那麼能夠將數據乘以一百萬而後使用BIGINT存儲。這樣能夠避免浮點數計算不許確和DECIMAL精確計算代價高的問題。
  5. TIMESTAMP使用4個字節存儲空間,DATETIME使用8個字節存儲空間。於是,TIMESTAMP只能表示1970 - 2038年,比DATETIME表示的範圍小得多,並且TIMESTAMP的值因時區不一樣而不一樣。
  6. 大多數狀況下沒有使用枚舉類型的必要,其中一個缺點是枚舉的字符串列表是固定的,添加和刪除字符串(枚舉選項)必須使用ALTER TABLE(若是隻只是在列表末尾追加元素,不須要重建表)。
  7. schema的列不要太多。緣由是存儲引擎的API工做時須要在服務器層和存儲引擎層之間經過行緩衝格式拷貝數據,而後在服務器層將緩衝內容解碼成各個列,這個轉換過程的代價是很是高的。若是列太多而實際使用的列又不多的話,有可能會致使CPU佔用太高。
  8. 大表ALTER TABLE很是耗時,MySQL執行大部分修改表結果操做的方法是用新的結構建立一個張空表,從舊錶中查出全部的數據插入新表,而後再刪除舊錶。尤爲當內存不足而表又很大,並且還有很大索引的狀況下,耗時更久。固然有一些奇技淫巧能夠解決這個問題,有興趣可自行查閱。
  • 建立高性能索引
    索引是提升MySQL查詢性能的一個重要途徑,但過多的索引可能會致使太高的磁盤使用率以及太高的內存佔用,從而影響應用程序的總體性能。應當儘可能避免過後纔想起添加索引,由於過後可能須要監控大量的SQL才能定位到問題所在,並且添加索引的時間確定是遠大於初始添加索引所須要的時間,可見索引的添加也是很是有技術含量的。

接下來將向你展現一系列建立高性能索引的策略,以及每條策略其背後的工做原理。但在此以前,先了解與索引相關的一些算法和數據結構,將有助於更好的理解後文的內容。java

索引相關的數據結構和算法

一般咱們所說的索引是指B-Tree索引,它是目前關係型數據庫中查找數據最爲經常使用和有效的索引,大多數存儲引擎都支持這種索引。使用B-Tree這個術語,是由於MySQL在CREATE TABLE或其它語句中使用了這個關鍵字,但實際上不一樣的存儲引擎可能使用不一樣的數據結構,好比InnoDB就是使用的B+Tree。python

B+Tree中的B是指balance,意爲平衡。須要注意的是,B+樹索引並不能找到一個給定鍵值的具體行,它找到的只是被查找數據行所在的頁,接着數據庫會把頁讀入到內存,再在內存中進行查找,最後獲得要查找的數據。程序員

在介紹B+Tree前,先了解一下二叉查找樹,它是一種經典的數據結構,其左子樹的值老是小於根的值,右子樹的值老是大於根的值,以下圖①。若是要在這課樹中查找值爲5的記錄,其大體流程:先找到根,其值爲6,大於5,因此查找左子樹,找到3,而5大於3,接着找3的右子樹,總共找了3次。一樣的方法,若是查找值爲8的記錄,也須要查找3次。因此二叉查找樹的平均查找次數爲(3 + 3 + 3 + 2 + 2 + 1) / 6 = 2.3次,而順序查找的話,查找值爲2的記錄,僅須要1次,但查找值爲8的記錄則須要6次,因此順序查找的平均查找次數爲:(1 + 2 + 3 + 4 + 5 + 6) / 6 = 3.3次,所以大多數狀況下二叉查找樹的平均查找速度比順序查找要快。redis

因爲二叉查找樹能夠任意構造,一樣的值,能夠構造出如圖②的二叉查找樹,顯然這棵二叉樹的查詢效率和順序查找差很少。若想二叉查找數的查詢性能最高,須要這棵二叉查找樹是平衡的,也即平衡二叉樹(AVL樹)。算法

平衡二叉樹首先須要符合二叉查找樹的定義,其次必須知足任何節點的兩個子樹的高度差不能大於1。顯然圖②不知足平衡二叉樹的定義,而圖①是一課平衡二叉樹。平衡二叉樹的查找性能是比較高的(性能最好的是最優二叉樹),查詢性能越好,維護的成本就越大。好比圖①的平衡二叉樹,當用戶須要插入一個新的值9的節點時,就須要作出以下變更。數據庫

經過一次左旋操做就將插入後的樹從新變爲平衡二叉樹是最簡單的狀況了,實際應用場景中可能須要旋轉屢次。至此咱們能夠考慮一個問題,平衡二叉樹的查找效率還不錯,實現也很是簡單,相應的維護成本還能接受,爲何MySQL索引不直接使用平衡二叉樹?緩存

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

一種行之有效的解決方法是減小樹的深度,將二叉樹變爲m叉樹(多路搜索樹),而B+Tree就是一種多路搜索樹。理解B+Tree時,只須要理解其最重要的兩個特徵便可:第一,全部的關鍵字(能夠理解爲數據)都存儲在葉子節點(Leaf Page),非葉子節點(Index Page)並不存儲真正的數據,全部記錄節點都是按鍵值大小順序存放在同一層葉子節點上。其次,全部的葉子節點由指針鏈接。以下圖爲高度爲2的簡化了的B+Tree。

怎麼理解這兩個特徵?MySQL將每一個節點的大小設置爲一個頁的整數倍(緣由下文會介紹),也就是在節點空間大小必定的狀況下,每一個節點能夠存儲更多的內結點,這樣每一個結點能索引的範圍更大更精確。全部的葉子節點使用指針連接的好處是能夠進行區間訪問,好比上圖中,若是查找大於20而小於30的記錄,只須要找到節點20,就能夠遍歷指針依次找到2五、30。若是沒有連接指針的話,就沒法進行區間查找。這也是MySQL使用B+Tree做爲索引存儲結構的重要緣由。

MySQL爲什麼將節點大小設置爲頁的整數倍,這就須要理解磁盤的存儲原理。磁盤自己存取就比主存慢不少,在加上機械運動損耗(特別是普通的機械硬盤),磁盤的存取速度每每是主存的幾百萬分之一,爲了儘可能減小磁盤I/O,磁盤每每不是嚴格按需讀取,而是每次都會預讀,即便只須要一個字節,磁盤也會從這個位置開始,順序向後讀取必定長度的數據放入內存,預讀的長度通常爲頁的整數倍。

頁是計算機管理存儲器的邏輯塊,硬件及OS每每將主存和磁盤存儲區分割爲連續的大小相等的塊,每一個存儲塊稱爲一頁(許多OS中,頁的大小一般爲4K)。主存和磁盤以頁爲單位交換數據。當程序要讀取的數據不在主存中時,會觸發一個缺頁異常,此時系統會向磁盤發出讀盤信號,磁盤會找到數據的起始位置並向後連續讀取一頁或幾頁載入內存中,而後一塊兒返回,程序繼續運行。
MySQL巧妙利用了磁盤預讀原理,將一個節點的大小設爲等於一個頁,這樣每一個節點只須要一次I/O就能夠徹底載入。爲了達到這個目的,每次新建節點時,直接申請一個頁的空間,這樣就保證一個節點物理上也存儲在一個頁裏,加之計算機存儲分配都是按頁對齊的,就實現了讀取一個節點只需一次I/O。假設B+Tree的高度爲h,一次檢索最多須要h-1次I/O(根節點常駐內存),複雜度O(h) = O(logmN)。實際應用場景中,M一般較大,經常超過100,所以樹的高度通常都比較小,一般不超過3。

最後簡單瞭解下B+Tree節點的操做,在總體上對索引的維護有一個大概的瞭解,雖然索引能夠大大提升查詢效率,但維護索引仍要花費很大的代價,所以合理的建立索引也就尤其重要。

仍以上面的樹爲例,咱們假設每一個節點只能存儲4個內節點。首先要插入第一個節點28,以下圖所示。

leaf page和index page都沒有滿
接着插入下一個節點70,在Index Page中查詢後得知應該插入到50 - 70之間的葉子節點,但葉子節點已滿,這時候就須要進行也分裂的操做,當前的葉子節點起點爲50,因此根據中間值來拆分葉子節點,以下圖所示。

Leaf Page拆分
最後插入一個節點95,這時候Index Page和Leaf Page都滿了,就須要作兩次拆分,以下圖所示。

Leaf Page與Index Page拆分
拆分後最終造成了這樣一顆樹。

最終樹
B+Tree爲了保持平衡,對於新插入的值須要作大量的拆分頁操做,而頁的拆分須要I/O操做,爲了儘量的減小頁的拆分操做,B+Tree也提供了相似於平衡二叉樹的旋轉功能。當Leaf Page已滿但其左右兄弟節點沒有滿的狀況下,B+Tree並不急於去作拆分操做,而是將記錄移到當前所在頁的兄弟節點上。一般狀況下,左兄弟會被先檢查用來作旋轉操做。就好比上面第二個示例,當插入70的時候,並不會去作頁拆分,而是左旋操做。

左旋操做
經過旋轉操做能夠最大限度的減小頁分裂,從而減小索引維護過程當中的磁盤的I/O操做,也提升索引維護效率。須要注意的是,刪除節點跟插入節點相似,仍然須要旋轉和拆分操做,這裏就再也不說明。

  • 高性能策略
    經過上文,相信你對B+Tree的數據結構已經有了大體的瞭解,但MySQL中索引是如何組織數據的存儲呢?以一個簡單的示例來講明,假若有以下數據表:
CREATE TABLE People(
    last_name varchar(50) not null, first_name varchar(50) not null, dob date not null, gender enum(`m`,`f`) not null, key(last_name,first_name,dob) ); 

對於表中每一行數據,索引中包含了last_name、first_name、dob列的值,下圖展現了索引是如何組織數據存儲的。

索引如何組織數據存儲,來自:高性能MySQL
能夠看到,索引首先根據第一個字段來排列順序,當名字相同時,則根據第三個字段,即出生日期來排序,正是由於這個緣由,纔有了索引的「最左原則」。

  1. MySQL不會使用索引的狀況:非獨立的列
    「獨立的列」是指索引列不能是表達式的一部分,也不能是函數的參數。好比:
    select * from where id + 1 = 5
    咱們很容易看出其等價於 id = 4,可是MySQL沒法自動解析這個表達式,使用函數是一樣的道理。

  2. 前綴索引
    若是列很長,一般能夠索引開始的部分字符,這樣能夠有效節約索引空間,從而提升索引效率。

  3. 多列索引和索引順序
    在多數狀況下,在多個列上創建獨立的索引並不能提升查詢性能。理由很是簡單,MySQL不知道選擇哪一個索引的查詢效率更好,因此在老版本,好比MySQL5.0以前就會隨便選擇一個列的索引,而新的版本會採用合併索引的策略。舉個簡單的例子,在一張電影演員表中,在actor_id和film_id兩個列上都創建了獨立的索引,而後有以下查詢:

select film_id,actor_id from film_actor where actor_id = 1 or film_id = 1 

老版本的MySQL會隨機選擇一個索引,但新版本作以下的優化:

select film_id,actor_id from film_actor where actor_id = 1 union all select film_id,actor_id from film_actor where film_id = 1 and actor_id <> 1 

當出現多個索引作相交操做時(多個AND條件),一般來講一個包含全部相關列的索引要優於多個獨立索引。

當出現多個索引作聯合操做時(多個OR條件),對結果集的合併、排序等操做須要耗費大量的CPU和內存資源,特別是當其中的某些索引的選擇性不高,須要返回合併大量數據時,查詢成本更高。因此這種狀況下還不如走全表掃描。

所以explain時若是發現有索引合併(Extra字段出現Using union),應該好好檢查一下查詢和表結構是否是已是最優的,若是查詢和表都沒有問題,那隻能說明索引建的很是糟糕,應當慎重考慮索引是否合適,有可能一個包含全部相關列的多列索引更適合。

前面咱們提到過索引如何組織數據存儲的,從圖中能夠看到多列索引時,索引的順序對於查詢是相當重要的,很明顯應該把選擇性更高的字段放到索引的前面,這樣經過第一個字段就能夠過濾掉大多數不符合條件的數據。

索引選擇性是指不重複的索引值和數據表的總記錄數的比值,選擇性越高查詢效率越高,由於選擇性越高的索引可讓MySQL在查詢時過濾掉更多的行。惟一索引的選擇性是1,這是最好的索引選擇性,性能也是最好的。

理解索引選擇性的概念後,就不難肯定哪一個字段的選擇性較高了,查一下就知道了,好比:

SELECT * FROM payment where staff_id = 2 and customer_id = 584 

是應該建立(staff_id,customer_id)的索引仍是應該顛倒一下順序?執行下面的查詢,哪一個字段的選擇性更接近1就把哪一個字段索引前面就好。

select count(distinct staff_id)/count(*) as staff_id_selectivity, count(distinct customer_id)/count(*) as customer_id_selectivity, count(*) from payment 

多數狀況下使用這個原則沒有任何問題,但仍然注意你的數據中是否存在一些特殊狀況。舉個簡單的例子,好比要查詢某個用戶組下有過交易的用戶信息:

select user_id from trade where user_group_id = 1 and trade_amount > 0 

MySQL爲這個查詢選擇了索引(user_group_id,trade_amount),若是不考慮特殊狀況,這看起來沒有任何問題,但實際狀況是這張表的大多數數據都是從老系統中遷移過來的,因爲新老系統的數據不兼容,因此就給老系統遷移過來的數據賦予了一個默認的用戶組。這種狀況下,經過索引掃描的行數跟全表掃描基本沒什麼區別,索引也就起不到任何做用。

推廣開來講,經驗法則和推論在多數狀況下是有用的,能夠指導咱們開發和設計,但實際狀況每每會更復雜,實際業務場景下的某些特殊狀況可能會摧毀你的整個設計。

  1. 避免多個範圍條件
    實際開發中,咱們會常用多個範圍條件,好比想查詢某個時間段內登陸過的用戶:
select user.* from user where login_time > '2017-04-01' and age between 18 and 30; 

這個查詢有一個問題:它有兩個範圍條件,login_time列和age列,MySQL可使用login_time列的索引或者age列的索引,但沒法同時使用它們。

  1. 覆蓋索引
    若是一個索引包含或者說覆蓋全部須要查詢的字段的值,那麼就沒有必要再回表查詢,這就稱爲覆蓋索引。覆蓋索引是很是有用的工具,能夠極大的提升性能,由於查詢只須要掃描索引會帶來許多好處:

索引條目遠小於數據行大小,若是隻讀取索引,極大減小數據訪問量
索引是有按照列值順序存儲的,對於I/O密集型的範圍查詢要比隨機從磁盤讀取每一行數據的IO要少的多

  1. 使用索引掃描來排序
    MySQL有兩種方式能夠生產有序的結果集,其一是對結果集進行排序的操做,其二是按照索引順序掃描得出的結果天然是有序的。若是explain的結果中type列的值爲index表示使用了索引掃描來作排序。

掃描索引自己很快,由於只須要從一條索引記錄移動到相鄰的下一條記錄。但若是索引自己不能覆蓋全部須要查詢的列,那麼就不得不每掃描一條索引記錄就回表查詢一次對應的行。這個讀取操做基本上是隨機I/O,所以按照索引順序讀取數據的速度一般要比順序地全表掃描要慢。

在設計索引時,若是一個索引既可以知足排序,又知足查詢,是最好的。

只有當索引的列順序和ORDER BY子句的順序徹底一致,而且全部列的排序方向也同樣時,纔可以使用索引來對結果作排序。若是查詢須要關聯多張表,則只有ORDER BY子句引用的字段所有爲第一張表時,才能使用索引作排序。ORDER BY子句和查詢的限制是同樣的,都要知足最左前綴的要求(有一種狀況例外,就是最左的列被指定爲常數,下面是一個簡單的示例),其餘狀況下都須要執行排序操做,而沒法利用索引排序。

// 最左列爲常數,索引:(date,staff_id,customer_id) select staff_id,customer_id from demo where date = '2015-06-01' order by staff_id,customer_id 
  1. 冗餘和重複索引
    冗餘索引是指在相同的列上按照相同的順序建立的相同類型的索引,應當儘可能避免這種索引,發現後當即刪除。好比有一個索引(A,B),再建立索引(A)就是冗餘索引。冗餘索引常常發生在爲表添加新索引時,好比有人新建了索引(A,B),但這個索引不是擴展已有的索引(A)。

大多數狀況下都應該儘可能擴展已有的索引而不是建立新索引。但有極少狀況下出現性能方面的考慮須要冗餘索引,好比擴展已有索引而致使其變得過大,從而影響到其餘使用該索引的查詢。

  1. 刪除長期未使用的索引
    按期刪除一些長時間未使用過的索引是一個很是好的習慣。

關於索引這個話題打算就此打住,最後要說一句,索引並不老是最好的工具,只有當索引幫助提升查詢速度帶來的好處大於其帶來的額外工做時,索引纔是有效的。對於很是小的表,簡單的全表掃描更高效。對於中到大型的表,索引就很是有效。對於超大型的表,創建和維護索引的代價隨之增加,這時候其餘技術也許更有效,好比分區表。最後的最後,explain後再提測是一種美德。

  • 特定類型查詢優化

優化COUNT()查詢

COUNT()多是被你們誤解最多的函數了,它有兩種不一樣的做用,其一是統計某個列值的數量,其二是統計行數。統計列值時,要求列值是非空的,它不會統計NULL。若是確認括號中的表達式不可能爲空時,實際上就是在統計行數。最簡單的就是當使用COUNT(*)時,並非咱們所想象的那樣擴展成全部的列,實際上,它會忽略全部的列而直接統計行數。

咱們最多見的誤解也就在這兒,在括號內指定了一列卻但願統計結果是行數,並且還經常誤覺得前者的性能會更好。但實際並不是這樣,若是要統計行數,直接使用COUNT(*),意義清晰,且性能更好。

有時候某些業務場景並不須要徹底精確的COUNT值,能夠用近似值來代替,EXPLAIN出來的行數就是一個不錯的近似值,並且執行EXPLAIN並不須要真正地去執行查詢,因此成本很是低。一般來講,執行COUNT()都須要掃描大量的行才能獲取到精確的數據,所以很難優化,MySQL層面還能作得也就只有覆蓋索引了。若是不還能解決問題,只有從架構層面解決了,好比添加彙總表,或者使用redis這樣的外部緩存系統。

優化關聯查詢

在大數據場景下,表與表之間經過一個冗餘字段來關聯,要比直接使用JOIN有更好的性能。若是確實須要使用關聯查詢的狀況下,須要特別注意的是:

確保ON和USING字句中的列上有索引。在建立索引的時候就要考慮到關聯的順序。當表A和表B用列c關聯的時候,若是優化器關聯的順序是A、B,那麼就不須要在A表的對應列上建立索引。沒有用到的索引會帶來額外的負擔,通常來講,除非有其餘理由,只須要在關聯順序中的第二張表的相應列上建立索引(具體緣由下文分析)。

確保任何的GROUP BY和ORDER BY中的表達式只涉及到一個表中的列,這樣MySQL纔有可能使用索引來優化。

要理解優化關聯查詢的第一個技巧,就須要理解MySQL是如何執行關聯查詢的。當前MySQL關聯執行的策略很是簡單,它對任何的關聯都執行嵌套循環關聯操做,即先在一個表中循環取出單條數據,而後在嵌套循環到下一個表中尋找匹配的行,依次下去,直到找到全部表中匹配的行爲爲止。而後根據各個表匹配的行,返回查詢中須要的各個列。

太抽象了?以上面的示例來講明,好比有這樣的一個查詢:

SELECT A.xx,B.yy FROM A INNER JOIN B USING(c) WHERE A.xx IN (5,6) 

假設MySQL按照查詢中的關聯順序A、B來進行關聯操做,那麼能夠用下面的僞代碼表示MySQL如何完成這個查詢:

outer_iterator = SELECT A.xx,A.c FROM A WHERE A.xx IN (5,6); outer_row = outer_iterator.next; while(outer_row) { inner_iterator = SELECT B.yy FROM B WHERE B.c = outer_row.c; inner_row = inner_iterator.next; while(inner_row) { output[inner_row.yy,outer_row.xx]; inner_row = inner_iterator.next; } outer_row = outer_iterator.next; } 

能夠看到,最外層的查詢是根據A.xx列來查詢的,A.c上若是有索引的話,整個關聯查詢也不會使用。再看內層的查詢,很明顯B.c上若是有索引的話,可以加速查詢,所以只須要在關聯順序中的第二張表的相應列上建立索引便可。

優化LIMIT分頁

當須要分頁操做時,一般會使用LIMIT加上偏移量的辦法實現,同時加上合適的ORDER BY字句。若是有對應的索引,一般效率會不錯,不然,MySQL須要作大量的文件排序操做。

一個常見的問題是當偏移量很是大的時候,好比:LIMIT 10000 20這樣的查詢,MySQL須要查詢10020條記錄而後只返回20條記錄,前面的10000條都將被拋棄,這樣的代價很是高。

優化這種查詢一個最簡單的辦法就是儘量的使用覆蓋索引掃描,而不是查詢全部的列。而後根據須要作一次關聯查詢再返回全部的列。對於偏移量很大時,這樣作的效率會提高很是大。考慮下面的查詢:

SELECT film_id,description FROM film ORDER BY title LIMIT 50,5;

若是這張表很是大,那麼這個查詢最好改爲下面的樣子:

SELECT film.film_id,film.description FROM film INNER JOIN ( SELECT film_id FROM film ORDER BY title LIMIT 50,5 ) AS tmp USING(film_id); 

這裏的延遲關聯將大大提高查詢效率,讓MySQL掃描儘量少的頁面,獲取須要訪問的記錄後在根據關聯列回原表查詢所須要的列。

有時候若是可使用書籤記錄上次取數據的位置,那麼下次就能夠直接從該書籤記錄的位置開始掃描,這樣就能夠避免使用OFFSET,好比下面的查詢:

SELECT id FROM t LIMIT 10000, 10; 

改成:

SELECT id FROM t WHERE id > 10000 LIMIT 10; 

其餘優化的辦法還包括使用預先計算的彙總表,或者關聯到一個冗餘表,冗餘表中只包含主鍵列和須要作排序的列。

優化UNION

MySQL處理UNION的策略是先建立臨時表,而後再把各個查詢結果插入到臨時表中,最後再來作查詢。所以不少優化策略在UNION查詢中都沒有辦法很好的時候。常常須要手動將WHERE、LIMIT、ORDER BY等字句「下推」到各個子查詢中,以便優化器能夠充分利用這些條件先優化。

除非確實須要服務器去重,不然就必定要使用UNION ALL,若是沒有ALL關鍵字,MySQL會給臨時表加上DISTINCT選項,這會致使整個臨時表的數據作惟一性檢查,這樣作的代價很是高。固然即便使用ALL關鍵字,MySQL老是將結果放入臨時表,而後再讀出,再返回給客戶端。雖然不少時候沒有這個必要,好比有時候能夠直接把每一個子查詢的結果返回給客戶端。

結語

理解查詢是如何執行以及時間都消耗在哪些地方,再加上一些優化過程的知識,能夠幫助你們更好的理解MySQL,理解常見優化技巧背後的原理。但願本文中的原理、示例可以幫助你們更好的將理論和實踐聯繫起來,更多的將理論知識運用到實踐中。

其餘也沒啥說的了,給你們留兩個思考題吧,能夠在腦殼裏想一想答案,這也是你們常常掛在嘴邊的,但不多有人會思考爲何?

有很是多的程序員在分享時都會拋出這樣一個觀點:儘量不要使用存儲過程,存儲過程很是不容易維護,也會增長使用成本,應該把業務邏輯放到客戶端。既然客戶端都能幹這些事,那爲何還要存儲過程?

(存儲過程是通過編譯的一系列SQL語句的集合,若是用程序來實現,可能須要屢次鏈接數據庫,這樣下降了程序和數據庫的耦合。)

JOIN自己也挺方便的,直接查詢就行了,爲何還須要視圖呢?(視圖是一張虛擬表,簡化了用戶的操做,並且能夠對機密數據提供安全保護。)

相關文章
相關標籤/搜索