午夜视频在线网站,日韩视频精品在线,中文字幕精品一区二区三区在线,在线播放精品,1024你懂我懂的旧版人,欧美日韩一级黄色片,一区二区三区在线观看视频

分享

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

 習悟齋 2017-12-13

本文讓你重新理解并審視那些流行的優(yōu)化建議,并在實際業(yè)務(wù)場景下合理的運用。

說起 MySQL 的查詢優(yōu)化,相信大家收藏了一堆奇技淫巧:不能使用 SELECT *、不使用 NULL 字段、合理創(chuàng)建索引、為字段選擇合適的數(shù)據(jù)類型….. 你是否真的理解這些優(yōu)化技巧?是否理解其背后的工作原理?在實際場景下性能真有提升嗎?我想未必。因而理解這些優(yōu)化建議背后的原理就尤為重要,希望本文能讓你重新審視這些優(yōu)化建議,并在實際業(yè)務(wù)場景下合理的運用。小編推薦大家加一下這個群:103456743這個群里好幾千人了!大家遇到啥問題都會在里面交流!而且免費分享零基礎(chǔ)入門料資料web開發(fā) 爬蟲資料一整套!是個非常好的學習交流地方!也有程序員大神給大家熱心解答各種問題!很快滿員了。欲進從速哦!各種PDF等你來下載!全部都是免費的哦!只為幫助大家快速入門,所以小編在群里等你們過來一起交流學習呢!

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

MySQL 邏輯架構(gòu)

如果能在頭腦中構(gòu)建一幅 MySQL 各組件之間如何協(xié)同工作的架構(gòu)圖,有助于深入理解 MySQL 服務(wù)器。下圖展示了 MySQL 的邏輯架構(gòu)圖。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

MySQL 查詢過程

1. 客戶端 / 服務(wù)端通信協(xié)議

MySQL 客戶端 / 服務(wù)端通信協(xié)議是 “半雙工” 的:在任一時刻,要么是服務(wù)器向客戶端發(fā)送數(shù)據(jù),要么是客戶端向服務(wù)器發(fā)送數(shù)據(jù),這兩個動作不能同時發(fā)生。一旦一端開始發(fā)送消息,另一端要接收完整個消息才能響應(yīng)它,所以我們無法也無須將一個消息切成小塊獨立發(fā)送,也沒有辦法進行流量控制。

客戶端用一個單獨的數(shù)據(jù)包將查詢請求發(fā)送給服務(wù)器,所以當查詢語句很長的時候,需要設(shè)置 max_allowed_packet 參數(shù)。但是需要注意的是,如果查詢實在是太大,服務(wù)端會拒絕接收更多數(shù)據(jù)并拋出異常。

與之相反的是,服務(wù)器響應(yīng)給用戶的數(shù)據(jù)通常會很多,由多個數(shù)據(jù)包組成。但是當服務(wù)器響應(yīng)客戶端請求時,客戶端必須完整的接收整個返回結(jié)果,而不能簡單的只取前面幾條結(jié)果,然后讓服務(wù)器停止發(fā)送。因而在實際開發(fā)中,盡量保持查詢簡單且只返回必需的數(shù)據(jù),減小通信間數(shù)據(jù)包的大小和數(shù)量是一個非常好的習慣,這也是查詢中盡量避免使用 SELECT * 以及加上 LIMIT 限制的原因之一。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

  • 任何的查詢語句在開始之前都必須經(jīng)過檢查,即使這條 SQL 語句永遠不會命中緩存

  • 如果查詢結(jié)果可以被緩存,那么執(zhí)行完成后,會將結(jié)果存入緩存,也會帶來額外的系統(tǒng)消耗

基于此,我們要知道并不是什么情況下查詢緩存都會提高系統(tǒng)性能,緩存和失效都會帶來額外消耗,只有當緩存帶來的資源節(jié)約大于其本身消耗的資源時,才會給系統(tǒng)帶來性能提升。但要如何評估打開緩存是否能夠帶來性能提升是一件非常困難的事情,也不在本文討論的范疇內(nèi)。如果系統(tǒng)確實存在一些性能問題,可以嘗試打開查詢緩存,并在數(shù)據(jù)庫設(shè)計上做一些優(yōu)化,比如:

  • 用多個小表代替一個大表,注意不要過度設(shè)計

  • 批量插入代替循環(huán)單條插入

  • 合理控制緩存空間大小,一般來說其大小設(shè)置為幾十兆比較合適

  • 可以通過 SQL_CACHE 和 SQL_NO_CACHE 來控制某個查詢語句是否需要進行緩存

最后的忠告是不要輕易打開查詢緩存,特別是寫密集型應(yīng)用。如果你實在是忍不住,可以將 query_cache_type 設(shè)置為 DEMAND,這時只有加入 SQL_CACHE 的查詢才會走緩存,其他查詢則不會,這樣可以非常自由地控制哪些查詢需要被緩存。

當然查詢緩存系統(tǒng)本身是非常復雜的,這里討論的也只是很小的一部分,其他更深入的話題,比如:緩存是如何使用內(nèi)存的?如何控制內(nèi)存的碎片化?事務(wù)對查詢緩存有何影響等等,讀者可以自行閱讀相關(guān)資料,這里權(quán)當拋磚引玉吧。

3. 語法解析和預(yù)處理

MySQL 通過關(guān)鍵字將 SQL 語句進行解析,并生成一棵對應(yīng)的解析樹。這個過程解析器主要通過語法規(guī)則來驗證和解析。比如 SQL 中是否使用了錯誤的關(guān)鍵字或者關(guān)鍵字的順序是否正確等等。預(yù)處理則會根據(jù) MySQL 規(guī)則進一步檢查解析樹是否合法。比如檢查要查詢的數(shù)據(jù)表和數(shù)據(jù)列是否存在等。

4. 查詢優(yōu)化

經(jīng)過前面的步驟生成的語法樹被認為是合法的了,并且由優(yōu)化器將其轉(zhuǎn)化成查詢計劃。多數(shù)情況下,一條查詢可以有很多種執(zhí)行方式,最后都返回相應(yīng)的結(jié)果。優(yōu)化器的作用就是找到這其中最好的執(zhí)行計劃。

MySQL 使用基于成本的優(yōu)化器,它嘗試預(yù)測一個查詢使用某種執(zhí)行計劃時的成本,并選擇其中成本最小的一個。在 MySQL 可以通過查詢當前會話的 last_query_cost 的值來得到其計算當前查詢的成本。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

5. 查詢執(zhí)行引擎

在完成解析和優(yōu)化階段以后,MySQL 會生成對應(yīng)的執(zhí)行計劃,查詢執(zhí)行引擎根據(jù)執(zhí)行計劃給出的指令逐步執(zhí)行得出結(jié)果。整個執(zhí)行過程的大部分操作均是通過調(diào)用存儲引擎實現(xiàn)的接口來完成,這些接口被稱為 handler API。查詢過程中的每一張表由一個 handler 實例表示。實際上,MySQL 在查詢優(yōu)化階段就為每一張表創(chuàng)建了一個 handler 實例,優(yōu)化器可以根據(jù)這些實例的接口來獲取表的相關(guān)信息,包括表的所有列名、索引統(tǒng)計信息等。存儲引擎接口提供了非常豐富的功能,但其底層僅有幾十個接口,這些接口像搭積木一樣完成了一次查詢的大部分操作。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

性能優(yōu)化建議

看了這么多,你可能會期待給出一些優(yōu)化手段,是的,下面會從 3 個不同方面給出一些優(yōu)化建議。但請等等,還有一句忠告要先送給你:不要聽信你看到的關(guān)于優(yōu)化的 “絕對真理”,包括本文所討論的內(nèi)容,而應(yīng)該是在實際的業(yè)務(wù)場景下通過測試來驗證你關(guān)于執(zhí)行計劃以及響應(yīng)時間的假設(shè)。

1. Scheme 設(shè)計與數(shù)據(jù)類型優(yōu)化

選擇數(shù)據(jù)類型只要遵循小而簡單的原則就好,越小的數(shù)據(jù)類型通常會更快,占用更少的磁盤、內(nèi)存,處理時需要的 CPU 周期也更少。越簡單的數(shù)據(jù)類型在計算時需要更少的 CPU 周期,比如,整型就比字符操作代價低,因而會使用整型來存儲 ip 地址,使用 DATETIME 來存儲時間,而不是使用字符串。

這里總結(jié)幾個可能容易理解錯誤的技巧:

  • 通常來說把可為 NULL 的列改為 NOT NULL 不會對性能提升有多少幫助,只是如果計劃在列上創(chuàng)建索引,就應(yīng)該將該列設(shè)置為 NOT NULL。

  • 對整數(shù)類型指定寬度,比如 INT(11),沒有任何卵用。INT 使用 32 位(4 個字節(jié))存儲空間,那么它的表示范圍已經(jīng)確定,所以 INT(1) 和 INT(20) 對于存儲和計算是相同的。

  • UNSIGNED 表示不允許負值,大致可以使正數(shù)的上限提高一倍。比如 TINYINT 存儲范圍是 - 128 ~ 127,而 UNSIGNED TINYINT 存儲的范圍卻是 0 – 255。

  • 通常來講,沒有太大的必要使用 DECIMAL 數(shù)據(jù)類型。即使是在需要存儲財務(wù)數(shù)據(jù)時,仍然可以使用 BIGINT。比如需要精確到萬分之一,那么可以將數(shù)據(jù)乘以一百萬然后使用 BIGINT 存儲。這樣可以避免浮點數(shù)計算不準確和 DECIMAL 精確計算代價高的問題。

  • TIMESTAMP 使用 4 個字節(jié)存儲空間,DATETIME 使用 8 個字節(jié)存儲空間。因而,TIMESTAMP 只能表示 1970 – 2038 年,比 DATETIME 表示的范圍小得多,而且 TIMESTAMP 的值因時區(qū)不同而不同。

  • 大多數(shù)情況下沒有使用枚舉類型的必要,其中一個缺點是枚舉的字符串列表是固定的,添加和刪除字符串(枚舉選項)必須使用 ALTER TABLE(如果只是在列表末尾追加元素,不需要重建表)。

  • schema 的列不要太多。原因是存儲引擎的 API 工作時需要在服務(wù)器層和存儲引擎層之間通過行緩沖格式拷貝數(shù)據(jù),然后在服務(wù)器層將緩沖內(nèi)容解碼成各個列,這個轉(zhuǎn)換過程的代價是非常高的。如果列太多而實際使用的列又很少的話,有可能會導致 CPU 占用過高。

  • 大表 ALTER TABLE 非常耗時,MySQL 執(zhí)行大部分修改表結(jié)果操作的方法是用新的結(jié)構(gòu)創(chuàng)建一個張空表,從舊表中查出所有的數(shù)據(jù)插入新表,然后再刪除舊表。尤其當內(nèi)存不足而表又很大,而且還有很大索引的情況下,耗時更久。當然有一些奇技淫巧可以解決這個問題,有興趣可自行查閱。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

二叉查找樹和平衡二叉樹

由于二叉查找樹可以任意構(gòu)造,同樣的值,可以構(gòu)造出如圖②的二叉查找樹,顯然這棵二叉樹的查詢效率和順序查找差不多。若想二叉查找數(shù)的查詢性能最高,需要這棵二叉查找樹是平衡的,也即平衡二叉樹(AVL 樹)。

平衡二叉樹首先需要符合二叉查找樹的定義,其次必須滿足任何節(jié)點的兩個子樹的高度差不能大于 1。顯然圖②不滿足平衡二叉樹的定義,而圖①是一課平衡二叉樹。平衡二叉樹的查找性能是比較高的(性能最好的是最優(yōu)二叉樹),查詢性能越好,維護的成本就越大。比如圖①的平衡二叉樹,當用戶需要插入一個新的值 9 的節(jié)點時,就需要做出如下變動。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

平衡二叉樹旋轉(zhuǎn)

通過一次左旋操作就將插入后的樹重新變?yōu)槠胶舛鏄涫亲詈唵蔚那闆r了,實際應(yīng)用場景中可能需要旋轉(zhuǎn)多次。至此我們可以考慮一個問題,平衡二叉樹的查找效率還不錯,實現(xiàn)也非常簡單,相應(yīng)的維護成本還能接受,為什么 MySQL 索引不直接使用平衡二叉樹?

隨著數(shù)據(jù)庫中數(shù)據(jù)的增加,索引本身大小隨之增加,不可能全部存儲在內(nèi)存中,因此索引往往以索引文件的形式存儲的磁盤上。這樣的話,索引查找過程中就要產(chǎn)生磁盤 I/O 消耗,相對于內(nèi)存存取,I/O 存取的消耗要高幾個數(shù)量級??梢韵胂笠幌乱豢脦装偃f節(jié)點的二叉樹的深度是多少?如果將這么大深度的一顆二叉樹放磁盤上,每讀取一個節(jié)點,需要一次磁盤的 I/O 讀取,整個查找的耗時顯然是不能夠接受的。那么如何減少查找過程中的 I/O 存取次數(shù)?

一種行之有效的解決方法是減少樹的深度,將二叉樹變?yōu)?m 叉樹(多路搜索樹),而 B+Tree 就是一種多路搜索樹。理解 B+Tree 時,只需要理解其最重要的兩個特征即可:第一,所有的關(guān)鍵字(可以理解為數(shù)據(jù))都存儲在葉子節(jié)點(Leaf Page),非葉子節(jié)點(Index Page)并不存儲真正的數(shù)據(jù),所有記錄節(jié)點都是按鍵值大小順序存放在同一層葉子節(jié)點上。其次,所有的葉子節(jié)點由指針連接。如下圖為高度為 2 的簡化了的 B+Tree。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

簡化 B+Tree

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

leaf page 和 index page 都沒有滿

接著插入下一個節(jié)點 70,在 Index Page 中查詢后得知應(yīng)該插入到 50 – 70 之間的葉子節(jié)點,但葉子節(jié)點已滿,這時候就需要進行也分裂的操作,當前的葉子節(jié)點起點為 50,所以根據(jù)中間值來拆分葉子節(jié)點,如下圖所示。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

Leaf Page 拆分

最后插入一個節(jié)點 95,這時候 Index Page 和 Leaf Page 都滿了,就需要做兩次拆分,如下圖所示。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

Leaf Page 與 Index Page 拆分

拆分后最終形成了這樣一顆樹。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

最終樹

B+Tree 為了保持平衡,對于新插入的值需要做大量的拆分頁操作,而頁的拆分需要 I/O 操作,為了盡可能的減少頁的拆分操作,B+Tree 也提供了類似于平衡二叉樹的旋轉(zhuǎn)功能。當 Leaf Page 已滿但其左右兄弟節(jié)點沒有滿的情況下,B+Tree 并不急于去做拆分操作,而是將記錄移到當前所在頁的兄弟節(jié)點上。通常情況下,左兄弟會被先檢查用來做旋轉(zhuǎn)操作。就比如上面第二個示例,當插入 70 的時候,并不會去做頁拆分,而是左旋操作。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

左旋操作

通過旋轉(zhuǎn)操作可以最大限度的減少頁分裂,從而減少索引維護過程中的磁盤的 I/O 操作,也提高索引維護效率。需要注意的是,刪除節(jié)點跟插入節(jié)點類似,仍然需要旋轉(zhuǎn)和拆分操作,這里就不再說明。

高性能策略

通過上文,相信你對 B+Tree 的數(shù)據(jù)結(jié)構(gòu)已經(jīng)有了大致的了解,但 MySQL 中索引是如何組織數(shù)據(jù)的存儲呢?以一個簡單的示例來說明,假如有如下數(shù)據(jù)表:

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

對于表中每一行數(shù)據(jù),索引中包含了 last_name、first_name、dob 列的值,下圖展示了索引是如何組織數(shù)據(jù)存儲的。

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

索引如何組織數(shù)據(jù)存儲,來自:高性能 MySQL

可以看到,索引首先根據(jù)第一個字段來排列順序,當名字相同時,則根據(jù)第三個字段,即出生日期來排序,正是因為這個原因,才有了索引的 “最左原則”。

1. MySQL 不會使用索引的情況:非獨立的列

“獨立的列” 是指索引列不能是表達式的一部分,也不能是函數(shù)的參數(shù)。比如:

select * from where id + 1 = 5

我們很容易看出其等價于 id = 4,但是 MySQL 無法自動解析這個表達式,使用函數(shù)是同樣的道理。

2. 前綴索引

如果列很長,通??梢运饕_始的部分字符,這樣可以有效節(jié)約索引空間,從而提高索引效率。

3. 多列索引和索引順序

在多數(shù)情況下,在多個列上建立獨立的索引并不能提高查詢性能。理由非常簡單,MySQL 不知道選擇哪個索引的查詢效率更好,所以在老版本,比如 MySQL5.0 之前就會隨便選擇一個列的索引,而新的版本會采用合并索引的策略。舉個簡單的例子,在一張電影演員表中,在 actor_id 和 film_id 兩個列上都建立了獨立的索引,然后有如下查詢:

老版本的 MySQL 會隨機選擇一個索引,但新版本做如下的優(yōu)化:

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

5. 覆蓋索引

如果一個索引包含或者說覆蓋所有需要查詢的字段的值,那么就沒有必要再回表查詢,這就稱為覆蓋索引。覆蓋索引是非常有用的工具,可以極大的提高性能,因為查詢只需要掃描索引會帶來許多好處:

  • 索引條目遠小于數(shù)據(jù)行大小,如果只讀取索引,極大減少數(shù)據(jù)訪問量

  • 索引是有按照列值順序存儲的,對于 I/O 密集型的范圍查詢要比隨機從磁盤讀取每一行數(shù)據(jù)的 IO 要少的多

6. 使用索引掃描來排序

MySQL 有兩種方式可以生產(chǎn)有序的結(jié)果集,其一是對結(jié)果集進行排序的操作,其二是按照索引順序掃描得出的結(jié)果自然是有序的。如果 explain 的結(jié)果中 type 列的值為 index 表示使用了索引掃描來做排序。

掃描索引本身很快,因為只需要從一條索引記錄移動到相鄰的下一條記錄。但如果索引本身不能覆蓋所有需要查詢的列,那么就不得不每掃描一條索引記錄就回表查詢一次對應(yīng)的行。這個讀取操作基本上是隨機 I/O,因此按照索引順序讀取數(shù)據(jù)的速度通常要比順序地全表掃描要慢。

在設(shè)計索引時,如果一個索引既能夠滿足排序,又滿足查詢,是最好的。

只有當索引的列順序和 ORDER BY 子句的順序完全一致,并且所有列的排序方向也一樣時,才能夠使用索引來對結(jié)果做排序。如果查詢需要關(guān)聯(lián)多張表,則只有 ORDER BY 子句引用的字段全部為第一張表時,才能使用索引做排序。ORDER BY 子句和查詢的限制是一樣的,都要滿足最左前綴的要求(有一種情況例外,就是最左的列被指定為常數(shù),下面是一個簡單的示例),其它情況下都需要執(zhí)行排序操作,而無法利用索引排序。

// 最左列為常數(shù),索引:(date,staff_id,customer_id)  select staff_id,customer_id from demo where date = '2015-06-01' order by staff_id,customer_id

7. 冗余和重復索引

冗余索引是指在相同的列上按照相同的順序創(chuàng)建的相同類型的索引,應(yīng)當盡量避免這種索引,發(fā)現(xiàn)后立即刪除。比如有一個索引 (A,B),再創(chuàng)建索引 (A) 就是冗余索引。冗余索引經(jīng)常發(fā)生在為表添加新索引時,比如有人新建了索引 (A,B),但這個索引不是擴展已有的索引 (A)。

大多數(shù)情況下都應(yīng)該盡量擴展已有的索引而不是創(chuàng)建新索引。但有極少情況下出現(xiàn)性能方面的考慮需要冗余索引,比如擴展已有索引而導致其變得過大,從而影響到其他使用該索引的查詢。

8. 刪除長期未使用的索引

定期刪除一些長時間未使用過的索引是一個非常好的習慣。

關(guān)于索引這個話題打算就此打住,最后要說一句,索引并不總是最好的工具,只有當索引幫助提高查詢速度帶來的好處大于其帶來的額外工作時,索引才是有效的。對于非常小的表,簡單的全表掃描更高效。對于中到大型的表,索引就非常有效。對于超大型的表,建立和維護索引的代價隨之增長,這時候其他技術(shù)也許更有效,比如分區(qū)表。最后的最后,explain 后再提測是一種美德。

特定類型查詢優(yōu)化

1. 優(yōu)化 COUNT() 查詢

COUNT() 可能是被大家誤解最多的函數(shù)了,它有兩種不同的作用,其一是統(tǒng)計某個列值的數(shù)量,其二是統(tǒng)計行數(shù)。統(tǒng)計列值時,要求列值是非空的,它不會統(tǒng)計 NULL。如果確認括號中的表達式不可能為空時,實際上就是在統(tǒng)計行數(shù)。最簡單的就是當使用 COUNT(*) 時,并不是我們所想象的那樣擴展成所有的列,實際上,它會忽略所有的列而直接統(tǒng)計所有的行數(shù)。

我們最常見的誤解也就在這兒,在括號內(nèi)指定了一列卻希望統(tǒng)計結(jié)果是行數(shù),而且還常常誤以為前者的性能會更好。但實際并非這樣,如果要統(tǒng)計行數(shù),直接使用 COUNT(*),意義清晰,且性能更好。

有時候某些業(yè)務(wù)場景并不需要完全精確的 COUNT 值,可以用近似值來代替,EXPLAIN 出來的行數(shù)就是一個不錯的近似值,而且執(zhí)行 EXPLAIN 并不需要真正地去執(zhí)行查詢,所以成本非常低。通常來說,執(zhí)行 COUNT() 都需要掃描大量的行才能獲取到精確的數(shù)據(jù),因此很難優(yōu)化,MySQL 層面還能做得也就只有覆蓋索引了。如果不還能解決問題,只有從架構(gòu)層面解決了,比如添加匯總表,或者使用 redis 這樣的外部緩存系統(tǒng)。

2. 優(yōu)化關(guān)聯(lián)查詢

在大數(shù)據(jù)場景下,表與表之間通過一個冗余字段來關(guān)聯(lián),要比直接使用 JOIN 有更好的性能。如果確實需要使用關(guān)聯(lián)查詢的情況下,需要特別注意的是:

  • 確保 ON 和 USING 字句中的列上有索引。在創(chuàng)建索引的時候就要考慮到關(guān)聯(lián)的順序。當表 A 和表 B 用列 c 關(guān)聯(lián)的時候,如果優(yōu)化器關(guān)聯(lián)的順序是 A、B,那么就不需要在 A 表的對應(yīng)列上創(chuàng)建索引。沒有用到的索引會帶來額外的負擔,一般來說,除非有其他理由,只需要在關(guān)聯(lián)順序中的第二張表的相應(yīng)列上創(chuàng)建索引(具體原因下文分析)。

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

要理解優(yōu)化關(guān)聯(lián)查詢的第一個技巧,就需要理解 MySQL 是如何執(zhí)行關(guān)聯(lián)查詢的。當前 MySQL 關(guān)聯(lián)執(zhí)行的策略非常簡單,它對任何的關(guān)聯(lián)都執(zhí)行嵌套循環(huán)關(guān)聯(lián)操作,即先在一個表中循環(huán)取出單條數(shù)據(jù),然后在嵌套循環(huán)到下一個表中尋找匹配的行,依次下去,直到找到所有表中匹配的行為為止。然后根據(jù)各個表匹配的行,返回查詢中需要的各個列。

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

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

可以看到,最外層的查詢是根據(jù) A.xx 列來查詢的,A.c 上如果有索引的話,整個關(guān)聯(lián)查詢也不會使用。再看內(nèi)層的查詢,很明顯 B.c 上如果有索引的話,能夠加速查詢,因此只需要在關(guān)聯(lián)順序中的第二張表的相應(yīng)列上創(chuàng)建索引即可。

3. 優(yōu) 化 LIMIT 分頁

當需要分頁操作時,通常會使用 LIMIT 加上偏移量的辦法實現(xiàn),同時加上合適的 ORDER BY 字句。如果有對應(yīng)的索引,通常效率會不錯,否則,MySQL 需要做大量的文件排序操作。

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

優(yōu)化這種查詢一個最簡單的辦法就是盡可能的使用覆蓋索引掃描,而不是查詢所有的列。然后根據(jù)需要做一次關(guān)聯(lián)查詢再返回所有的列。對于偏移量很大時,這樣做的效率會提升非常大。考慮下面的查詢:

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

如果這張表非常大,那么這個查詢最好改成下面的樣子:

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

4. 優(yōu)化 UNION

MySQL 處理 UNION 的策略是先創(chuàng)建臨時表,然后再把各個查詢結(jié)果插入到臨時表中,最后再來做查詢。因此很多優(yōu)化策略在 UNION 查詢中都沒有辦法很好的時候。經(jīng)常需要手動將 WHERE、LIMIT、ORDER BY 等字句 “下推” 到各個子查詢中,以便優(yōu)化器可以充分利用這些條件先優(yōu)化。

除非確實需要服務(wù)器去重,否則就一定要使用 UNION ALL,如果沒有 ALL 關(guān)鍵字,MySQL 會給臨時表加上 DISTINCT 選項,這會導致整個臨時表的數(shù)據(jù)做唯一性檢查,這樣做的代價非常高。當然即使使用 ALL 關(guān)鍵字,MySQL 總是將結(jié)果放入臨時表,然后再讀出,再返回給客戶端。雖然很多時候沒有這個必要,比如有時候可以直接把每個子查詢的結(jié)果返回給客戶端。

結(jié)語

理解查詢是如何執(zhí)行以及時間都消耗在哪些地方,再加上一些優(yōu)化過程的知識,可以幫助大家更好的理解 MySQL,理解常見優(yōu)化技巧背后的原理。希望本文中的原理、示例能夠幫助大家更好的將理論和實踐聯(lián)系起來,更多的將理論知識運用到實踐中。其他也沒啥說的了,給大家留兩個思考題吧,可以在腦袋里想想答案,這也是大家經(jīng)常掛在嘴邊的,但很少有人會思考為什么?

  • 有非常多的程序員在分享時都會拋出這樣一個觀點:盡可能不要使用存儲過程,存儲過程非常不容易維護,也會增加使用成本,應(yīng)該把業(yè)務(wù)邏輯放到客戶端。既然客戶端都能干這些事,那為什么還要存儲過程?

  • JOIN 本身也挺方便的,直接查詢就好了,為什么還需要視圖呢?

參考資料:

  • 姜承堯 著;MySQL 技術(shù)內(nèi)幕 - InnoDB 存儲引擎;機械工業(yè)出版社,2013

  • Baron Scbwartz 等著;寧海元 周振興等譯;高性能 MySQL(第三版); 電子工業(yè)出版社, 2013

謝謝閱讀;

神級程序員寫的萬字長文!超多干貨帶你搞懂MySQL優(yōu)化原理學習!

如有侵權(quán)請聯(lián)系小編刪除!

    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點。請注意甄別內(nèi)容中的聯(lián)系方式、誘導購買等信息,謹防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多