超詳細彙總mysql優化實踐技巧

2022-03-24 19:00:58
本篇文章給大家帶來了關於mysql的相關知識,其中主要總結了二十一個mysql優化的實踐技巧,下面就一起來看一下這二十一個優化技巧,希望對大家有幫助。

推薦學習:

今天,資料庫的操作越來越成為整個應用的效能瓶頸了,這點對於 Web 應用尤其明顯。關於資料庫的效能,這並不只是 DBA 才需要擔心的事,而這更是我們程式設計師需要去關注的事情。當我們去設計資料庫表結構,對運算元據庫時(尤其是查表時的 SQL 語句),我們都需要注意資料操作的效能。這裡,我們不會講過多的 SQL 語句的優化,而只是針對 MySQL 這一 Web 應用最多的資料庫。希望下面的這些優化技巧對你有用

1.為查詢快取優化你的查詢

大多數的 MySQL 伺服器都開啟了查詢快取。這是提高性最有效的方法之一,而且這是被 MySQL 的資料庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個快取中,這樣,後續的相同的查詢就不用操作表而直接存取快取結果了。
這裡最主要的問題是,對於程式設計師來說,這個事情是很容易被忽略的。因為,我們某些查詢語句會讓 MySQL 不使用快取。請看下面的範例:

在這裡插入圖片描述

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢快取對這個函數不起作用。所以,像 NOW()RAND() 或是其它的諸如此類的 SQL 函數都不會開啟查詢快取,因為這些函數的返回是會不定的易變的。所以,你所需要的就是用一個變數來代替 MySQL 的函數,從而開啟快取。

2. EXPLAIN 你的 SELECT 查詢

使用 EXPLAIN 關鍵字可以讓你知道 MySQL 是如何處理你的 SQL 語句的。這可以幫你分析你的查詢語句或是表結構的效能瓶頸。
EXPLAIN 的查詢結果還會告訴你你的索引主鍵被如何利用的,你的資料表是如何被搜尋和排序的……等等,等等。
挑一個你的 SELECT 語句(推薦挑選那個最複雜的,有多表聯接的),把關鍵字 EXPLAIN 加到前面。你可以使用 phpmyadmin 來做這個事。然後,你會看到一張表格。下面的這個範例中,我們忘記加上了 group_id 索引,並且有表聯接:

在這裡插入圖片描述

當我們為 group_id 欄位加上索引後:
在這裡插入圖片描述
我們可以看到,前一個結果顯示搜尋了 7883 行,而後一個只是搜尋了兩個表的 9 和 16 行。檢視 rows 列可以讓我們找到潛在的效能問題。

3. 當只要一行資料時使用 LIMIT 1 1

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去 fetch 遊標,或是你也許會去檢查返回的記錄數。
在這種情況下,加上 LIMIT 1 可以增加效能。這樣一樣,MySQL 資料庫引擎會在找到一條資料後停止搜尋,而不是繼續往後查少下一條符合記錄的資料。
下面的範例,只是為了找一下是否有「中國」的使用者,很明顯,後面的會比前面的更有效率。(請注意,第一條中是 Select *,第二條是 Select 1)

在這裡插入圖片描述

4. 為搜尋欄位建索引

索引並不一定就是給主鍵或是唯一的欄位。如果在你的表中,有某個欄位你總要會經常用來做搜尋,那麼,請為其建立索引吧

在這裡插入圖片描述

從上圖你可以看到那個搜尋字串 「last_name LIKE ‘a%’」,一個是建了索引,一個是沒有索引,效能差了 4 倍左右。
另外,你應該也需要知道什麼樣的搜尋是不能使用正常的索引的。例如,當你需要在一篇大的文章中搜尋一個詞時,如: 「WHERE post_content LIKE ‘%apple%’」,索引可能是沒有意義的。你可能需要使用 MySQL 全文索引 或是自己做一個索引(比如說:搜尋關鍵詞或是 Tag 什麼的)

5. 在 Join 表的時候使用相同型別的例

如果你的應用程式有很多 JOIN 查詢,你應該確認兩個表中 Join 的欄位是被建過索引的。這樣,MySQL 內部會啟動為你優化 Join 的 SQL 語句的機制。
而且,這些被用來 Join 的欄位,應該是相同的型別的。例如:如果你要把DECIMAL 欄位和一個 INT 欄位 Join 在一起,MySQL 就無法使用它們的索引。對於那些 STRING 型別,還需要有相同的字元集才行。(兩個表的字元集有可能不一樣)

在這裡插入圖片描述

6. 千萬不要 ORDER BY RAND()

想打亂返回的資料行?隨機挑一個資料?真不知道誰發明了這種用法,但很多新手很喜歡這樣用。但你確不瞭解這樣做有多麼可怕的效能問題。
如果你真的想把返回的資料行打亂了,你有 N 種方法可以達到這個目的。這樣使用只讓你的資料庫的效能呈指數級的下降。這裡的問題是:MySQL 會不得不去執行 RAND()函數(很耗 CPU 時間),而且這是為了每一行記錄去記行,然後再對其排序。就算是你用了 Limit 1 也無濟於事(因為要排序)

下面的範例是隨機挑一條記錄:
在這裡插入圖片描述

7.避免 SELECT *

從資料庫裡讀出越多的資料,那麼查詢就會變得越慢。並且,如果你的資料庫伺服器和 WEB 伺服器是兩臺獨立的伺服器的話,這還會增加網路傳輸的負載。所以,你應該養成一個需要什麼就取什麼的好的習慣
在這裡插入圖片描述

8. 永遠為每張表設定一個 ID

我們應該為資料庫裡的每張表都設定一個 ID 做為其主鍵,而且最好的是一個 INT 型的(推薦使用 UNSIGNED),並設定上自動增加的 AUTO_INCREMENT 標誌。
就算是你 users 表有一個主鍵叫 「email」的欄位,你也別讓它成為主鍵。使用 VARCHAR 型別來當主鍵會使用得效能下降。另外,在你的程式中,你應該使用表的 ID 來構造你的資料結構。
而且,在 MySQL 資料引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的效能和設定變得非常重要,比如,叢集,分割區……
在這裡,只有一個情況是例外,那就是「關聯表」的「外來鍵」,也就是說,這個表的主鍵,通過若干個別的表的主鍵構成。我們把這個情況叫做「外來鍵」。比如:有一個「學生表」有學生的 ID,有一個「課程表」有課程 ID,那麼,「成績表」就是「關聯表」了,其關聯了學生表和課程表,在成績表中,學生 ID 和課程 ID 叫「外來鍵」其共同組成主鍵。

9. 使用 ENUM 而不是 VARCHAR

ENUM 型別是非常快和緊湊的。在實際上,其儲存的是 TINYINT,但其外表上顯示為字串。這樣一來,用這個欄位來做一些選項列表變得相當的完美。
如果你有一個欄位,比如「性別」,「國家」,「民族」,「狀態」或「部門」,你知道這些欄位的取值是有限而且固定的,那麼,你應該使用 ENUM而不是 VARCHAR
MySQL 也有一個「建議」(見第十條)告訴你怎麼去重新組織你的表結構。當你有一個 VARCHAR 欄位時,這個建議會告訴你把其改成 ENUM 型別。使用PROCEDURE ANALYSE() 你可以得到相關的建議

10. 從 PROCEDURE ANALYSE() 取得建議

PROCEDURE ANALYSE() 會讓 MySQL 幫你去分析你的欄位和其實際的資料,並會給你一些有用的建議。只有表中有實際的資料,這些建議才會變得有用,因為要做一些大的決定是需要有資料作為基礎的。
例如,如果你建立了一個 INT 欄位作為你的主鍵,然而並沒有太多的資料,那麼,PROCEDURE ANALYSE()會建議你把這個欄位的型別改成 MEDIUMINT 。或是你使用了一個 VARCHAR 欄位,因為資料不多,你可能會得到一個讓你把它
改成 ENUM 的建議。這些建議,都是可能因為資料不夠多,所以決策做得就不夠準。
phpmyadmin 裡,你可以在檢視表時,點選 「Propose table structure」 來檢視這些建議

在這裡插入圖片描述

一定要注意,這些只是建議,只有當你的表裡的資料越來越多時,這些建議才會變得準確。一定要記住,你才是最終做決定的人

11. 儘可能的使用 NOT NULL

除非你有一個很特別的原因去使用 NULL 值,你應該總是讓你的欄位保持NOT NULL。這看起來好像有點爭議,請往下看。
首先,問問你自己「Empty」和「NULL」有多大的區別(如果是 INT,那就是 0 和 NULL)?如果你覺得它們之間沒有什麼區別,那麼你就不要使用 NULL。(你知道嗎?在 Oracle 裡,NULLEmpty 的字串是一樣的!)
不要以為 NULL 不需要空間,其需要額外的空間,並且,在你進行比較的時候,你的程式會更復雜。 當然,這裡並不是說你就不能使用 NULL 了,現實情況是很複雜的,依然會有些情況下,你需要使用 NULL 值。

12.Prepared Statements

Prepared Statements 很像儲存過程,是一種執行在後臺的 SQL 語句集合,我們可以從使用 prepared statements 獲得很多好處,無論是效能問題還是安全問題
Prepared Statements 可以檢查一些你係結好的變數,這樣可以保護你的程式不會受到「SQL 注入式」攻擊。當然,你也可以手動地檢查你的這些變數,然而,手動的檢查容易出問題,而且很經常會被程式設計師忘了。當我們使用一些framework 或是 ORM 的時候,這樣的問題會好一些。
效能方面,當一個相同的查詢被使用多次的時候,這會為你帶來可觀的效能優勢。你可以給這些 Prepared Statements 定義一些引數,而 MySQL 只會解析一次。
雖然最新版本的 MySQL 在傳輸 Prepared Statements 是使用二進位制形式,所以這會使得網路傳輸非常有效率
當然,也有一些情況下,我們需要避免使用 Prepared Statements,因為其不支援查詢快取。但據說版本 5.1 後支援了。
在 PHP 中要使用 prepared statements,你可以檢視其使用手冊:mysql擴充套件 或是使用資料庫抽象層,如: PDO.

在這裡插入圖片描述

13. 無緩衝的查詢

正常的情況下,當你在當你在你的指令碼中執行一個 SQL 語句的時候,你的程式會停在那裡直到沒這個 SQL 語句返回,然後你的程式再往下繼續執行。你可以使用無緩衝查詢來改變這個行為。
mysql_unbuffered_query() 傳送一個 SQL 語句到 MySQL 而並不像mysql_query()一樣去自動 fethch 和快取結果。這會相當節約很多可觀的記憶體,尤其是那些會產生大量結果的查詢語句,並且,你不需要等到所有的結果都返回,只需要第一行資料返回的時候,你就可以開始馬上開始工作於查詢結果了。
然而,這會有一些限制。因為你要麼把所有行都讀走,或是你要在進行下一次的查詢前呼叫 mysql_free_result() 清除結果。而且, mysql_num_rows()或 mysql_data_seek() 將無法使用。所以,是否使用無緩衝的查詢你需要仔細考慮。

14. 把 IP 地址存成 UNSIGNED INT

很多程式設計師都會建立一個 VARCHAR(15) 欄位來存放字串形式的 IP 而不是整形的 IP。如果你用整形來存放,只需要 4 個位元組,並且你可以有定長的欄位。而且,這會為你帶來查詢上的優勢,尤其是當你需要使用這樣的 WHERE 條件:IP between ip1 and ip2
我們必需要使用 UNSIGNED INT因為 IP 地址會使用整個 32 位的無符號整型。
而你的查詢,你可以使用 INET_ATON() 來把一個字串 IP 轉成一個整型,並使用 INET_NTOA() 把一個整形轉成一個字串 IP。在 PHP 中,也有這樣的函數 ip2long() 和 long2ip()
在這裡插入圖片描述

15. 固定長度的表會更快

如果表中的所有欄位都是「固定長度」的,整個表會被認為是 「static」或 「fixed-length」。 例如,表中沒有如下型別的欄位: VARCHAR,TEXT。只要你包括了其中一個這些欄位,那麼這個表就不是「固定長度靜態表」了,這樣,MySQL 引擎會用另一種方法來處理。
固定長度的表會提高效能,因為 MySQL 搜尋得會更快一些,因為這些固定的長度是很容易計算下一個資料的偏移量的,所以讀取的自然也會很快。而如果欄位不是定長的,那麼,每一次要找下一條的話,需要程式找到主鍵。
並且,固定長度的表也更容易被快取和重建。不過,唯一的副作用是,固定長度的欄位會浪費一些空間因為定長的欄位無論你用不用,他都是要分配那麼多的空間。

使用「垂直分割」技術(見下一條),你可以分割你的表成為兩個一個是定長的,一個則是不定長的。

16. 垂直分割

「垂直分割」是一種把資料庫中的表按列變成幾張表的方法,這樣可以降低表的複雜度和欄位的數目,從而達到優化的目的。(以前,在銀行做過專案,見過一張表有 100 多個欄位,很恐怖)

範例一:在 Users 表中有一個欄位是家庭地址,這個欄位是可選欄位,相比起,而且你在資料庫操作的時候除了個人資訊外,你並不需要經常讀取或是改寫這個欄位。那麼,為什麼不把他放到另外一張表中呢? 這樣會讓你的表有更好的效能,大家想想是不是,大量的時候,我對於使用者表來說,只有使用者ID,使用者名稱,口令,使用者角色等會被經常使用。小一點的表總是會有好的性
能。

範例二: 你有一個叫 「last_login」 的欄位,它會在每次使用者登入時被更新。但是,每次更新時會導致該表的查詢快取被清空。所以,你可以把這個欄位放到另一個表中,這樣就不會影響你對使用者 ID,使用者名稱,使用者角色的不停地讀取了,因為查詢快取會幫你增加很多效能。

另外,你需要注意的是,這些被分出去的欄位所形成的表,你不會經常性地去 Join 他們,不然的話,這樣的效能會比不分割時還要差,而且,會是極數級的下降

17.拆分大的 DELETE 或 INSERT 語句

如果你需要在一個線上的網站上去執行一個大的 DELETEINSERT 查詢,你需要非常小心,要避免你的操作讓你的整個網站停止相應。因為這兩個操作是會鎖表的,表一鎖住了,別的操作都進不來了。
Apache 會有很多的子程序或執行緒。所以,其工作起來相當有效率,而我們的伺服器也不希望有太多的子程序,執行緒和資料庫連結,這是極大的佔伺服器資源的事情,尤其是記憶體。
如果你把你的表鎖上一段時間,比如 30 秒鐘,那麼對於一個有很高存取量的站點來說,這 30 秒所積累的存取程序/執行緒,資料庫連結,開啟的檔案數,可能不僅僅會讓你泊 WEB 服務 Crash,還可能會讓你的整臺伺服器馬上掛了。
所以,如果你有一個大的處理,你定你一定把其拆分,使用 LIMIT 條件是一個好的方法。下面是一個範例:

在這裡插入圖片描述

18.越小的列會越快

對於大多數的資料庫引擎來說,硬碟操作可能是最重大的瓶頸。所以,把你的資料變得緊湊會對這種情況非常有幫助,因為這減少了對硬碟的存取。
參看 MySQL 的檔案 Storage Requirements 檢視所有的資料型別。
如果一個表只會有幾列罷了(比如說字典表,設定表),那麼,我們就沒有理由使用 INT 來做主鍵,使用 MEDIUMINT, SMALLINT 或是更小的 TINYINT 會更經濟一些。如果你不需要記錄時間,使用 DATE 要比 DATETIME 好得多。
當然,你也需要留夠足夠的擴充套件空間,不然,你日後來幹這個事,你會死的很難看,參看 Slashdot 的例子(2009 年 11 月 06 日),一個簡單的 ALTER TABLE 語句花了 3 個多小時,因為裡面有一千六百萬條資料。

19. 選擇正確的儲存引擎

在 MySQL 中有兩個儲存引擎 MyISAM 和 InnoDB,每個引擎都有利有弊。酷殼以前文章《MySQL: InnoDB 還是 MyISAM?》討論和這個事情。
MyISAM 適合於一些需要大量查詢的應用,但其對於有大量寫操作並不是很好。甚至你只是需要 update 一個欄位,整個表都會被鎖起來,而別的程序,就算是讀程序都無法操作直到讀操作完成。另外,MyISAM 對於 SELECT COUNT(*)這類的計算是超快無比的。
InnoDB 的趨勢會是一個非常複雜的儲存引擎,對於一些小的應用,它會比MyISAM 還慢。他是它支援「行鎖」 ,於是在寫操作比較多的時候,會更優秀。並且,他還支援更多的高階應用,比如:事務。

20. 使用一個物件關係對映器 (Object Relational Mapper)

使用 ORM (Object Relational Mapper),你能夠獲得可靠的效能增漲。一個ORM 可以做的所有事情,也能被手動的編寫出來。但是,這需要一個高階專家。
ORM 的最重要的是「Lazy Loading」,也就是說,只有在需要的去取值的時候才會去真正的去做。但你也需要小心這種機制的副作用,因為這很有可能會因為要去建立很多很多小的查詢反而會降低效能。
ORM 還可以把你的 SQL 語句打包成一個事務,這會比單獨執行他們快得多得多。
目前,個人最喜歡的 PHP 的 ORM 是:Doctrine

21. 小心 「 永久連結 」

「永久連結」的目的是用來減少重新建立 MySQL 連結的次數。當一個連結被建立了,它會永遠處在連線的狀態,就算是資料庫操作已經結束了。而且,自從我們的 Apache 開始重用它的子程序後——也就是說,下一次的 HTTP 請求會重用 Apache 的子程序,並重用相同的 MySQL 連結。
在理論上來說,這聽起來非常的不錯。但是從個人經驗(也是大多數人的)上來說,這個功能製造出來的麻煩事更多。因為,你只有有限的連結數,記憶體問題,檔案控制程式碼數,等等。
而且,Apache 執行在極端並行的環境中,會建立很多很多的了程序。這就是為什麼這種「永久連結」的機制工作地不好的原因。在你決定要使用「永久連結」之前,你需要好好地考慮一下你的整個系統的架構

22.sql優化之索引優化

1.獨立的列

在進行查詢時,索引列不能是表示式的一部分,也不能是函數的引數,否則無法使用索引。
例如下面的查詢不能使用 actor_id 列的索引:

#這是錯誤的SELECT actor_id FROM sakila.actor WHERE actor_id + 1 = 5;

優化方式:可以將表示式、函數操作移動到等號右側。如下:

SELECT actor_id FROM sakila.actor WHERE actor_id  = 5 - 1;

2.多列索引

在需要使用多個列作為條件進行查詢時,使用多列索引比使用多個單列索引效能更好。
例如下面的語句中,最好把actor_idfilm_id 設定為多列索引。猿輔導有道題,詳見連結,可以讓理解更深刻。

SELECT film_id, actor_ id FROM sakila.film_actorWHERE actor_id = 1 AND film_id = 1;

3.索引列的順序

讓選擇性最強的索引列放在前面。
索引的選擇性是指:不重複的索引值和記錄總數的比值。最大值為 1,此時每個記錄都有唯一的索引與其對應。選擇性越高,每個記錄的區分度越高,查詢效率也越高。

例如下面顯示的結果中 customer_id 的選擇性比 staff_id 更高,因此最好把 customer_id 列放在多列索引的前面。

SELECT COUNT(DISTINCT staff_id)/COUNT(*) AS staff_id_selectivity,
COUNT(DISTINCT customer_id)/COUNT(*) AS customer_id_selectivity,
COUNT(*)
FROM payment;

#結果如下
staff_id_selectivity: 0.0001
customer_id_selectivity: 0.0373
COUNT(*): 16049

4.字首索引

對於 BLOB、TEXT 和 VARCHAR 型別的列,必須使用字首索引,只索引開始的部分字元。

字首長度的選取需要根據索引選擇性來確定。

5.覆蓋索引

索引包含所有需要查詢的欄位的值。具有以下優點:

1.索引通常遠小於資料行的大小,唯讀取索引能大大減少資料存取量。
2.一些儲存引擎(例如 MyISAM)在記憶體中只快取索引,而資料依賴於作業系統來快取。因此,只存取索引可以不使用系統呼叫(通常比較費時)。
3.對於 InnoDB 引擎,若輔助索引能夠覆蓋查詢,則無需存取主索引。

6.優先使用索引,避免全表掃描

mysql在使用like進行模糊查詢的時候把%放後面,避免開頭模糊查詢
因為mysql在使用like查詢的時候只有使用後面的%時,才會使用到索引。

如:’%ptd_’ 和 ‘%ptd_%’ 都沒有用到索引;而 ‘ptd_%’ 使用了索引。

#進行全表查詢,沒有用到索引
EXPLAIN SELECT * FROM `user` WHERE username LIKE '%ptd_%';
EXPLAIN SELECT * FROM `user` WHERE username LIKE '%ptd_';

#有用到索引
EXPLAIN SELECT * FROM `user` WHERE username LIKE 'ptd_%';

再比如:經常用到的查詢資料庫中姓張的所有人:

SELECT * FROM `user` WHERE username LIKE '張%';

7.儘量避免使用in 和not in,會導致資料庫引擎放棄索引進行全表掃描。

比如:

SELECT * FROM t WHERE id IN (2,3)SELECT * FROM t1 WHERE username IN (SELECT username FROM t2)

優化方式:如果是連續數值,可以用between代替。如下:

SELECT * FROM t WHERE id BETWEEN 2 AND 3

如果是子查詢,可以用exists代替。如下:

SELECT * FROM t1 WHERE EXISTS (SELECT * FROM t2 WHERE t1.username = t2.username)

8.儘量避免使用or,會導致資料庫引擎放棄索引進行全表掃描

如:

SELECT * FROM t WHERE id = 1 OR id = 3

優化方式:可以用union代替or。如下:

SELECT * FROM t WHERE id = 1UNIONSELECT * FROM t WHERE id = 3

9.儘量避免進行null值的判斷,會導致資料庫引擎放棄索引進行全表掃描

SELECT * FROM t WHERE score IS NULL

優化方式:可以給欄位新增預設值0,對0值進行判斷。如下:

SELECT * FROM t WHERE score = 0

10.儘量避免在where條件中等號的左側進行表示式、函數操作,會導致資料庫引擎放棄索引進行全表掃描

同第1個,單獨的列;

SELECT * FROM t2 WHERE score/10 = 9SELECT * FROM t2 WHERE SUBSTR(username,1,2) = 'li'

優化方式:可以將表示式、函數操作移動到等號右側。如下:

SELECT * FROM t2 WHERE score = 10*9SELECT * FROM t2 WHERE username LIKE 'li%'

11.當資料量大時,避免使用where 1=1的條件。通常為了方便拼裝查詢條件,我們會預設使用該條件,資料庫引擎會放棄索引進行全表掃描

SELECT * FROM t WHERE 1=1

優化方式用程式碼拼裝sql時進行判斷,沒where加where,有where加and。
索引的好處:建立索引後,查詢時不會掃描全表,而會查詢索引表鎖定結果。
索引的缺點:在資料庫進行DML操作的時候,除了維護資料表之外,還需要維護索引表,運維成本增加。
應用場景:資料量比較大,查詢欄位較多的情況。

索引規則:

1.選用選擇性高的欄位作為索引,一般unique的選擇性最高;
2.複合索引:選擇性越高的排在越前面。(左字首原則);
3.如果查詢條件中兩個條件都是選擇性高的,最好都建索引;

23.SQL優化之查詢優化

1.使用Explain進行分析

Explain 用來分析 SELECT 查詢語句,開發人員可以通過分析 Explain 結果來優化查詢語句。

比較重要的欄位有:

select_type : 查詢型別,有簡單查詢、聯合查詢、子查詢等;
key : 使用的索引;
rows : 掃描的行數;

2.優化資料存取

1.減少請求的資料量

只返回必要的列:最好不要使用 SELECT * 語句。
只返回必要的行:使用 LIMIT 語句來限制返回的資料。
快取重複查詢的資料:使用快取可以避免在資料庫中進行查詢,特別在要查詢的資料經常被重複查詢時,快取帶來的查詢效能提升將會是非常明顯的。

2.減少伺服器端掃描的行數

最有效的方式是使用索引來覆蓋查詢。

3.重構查詢方式

1.切分大查詢

一個大查詢如果一次性執行的話,可能一次鎖住很多資料、佔滿整個事務紀錄檔、耗盡系統資源、阻塞很多小的但重要的查詢。

2.分解大連線查詢

將一個大連線查詢分解成對每一個表進行一次單表查詢,然後在應用程式中進行關聯,這樣做的好處有:

讓快取更高效:對於連線查詢,如果其中一個表發生變化,那麼整個查詢快取就無法使用。而分解後的多個查詢,即使其中一個表發生變化,對其它表的查詢快取依然可以使用。
分解成多個單表查詢,這些單表查詢的快取結果更可能被其它查詢使用到,從而減少冗餘記錄的查詢。
減少鎖競爭;
在應用層進行連線,可以更容易對資料庫進行拆分,從而更容易做到高效能和可伸縮。
查詢本身效率也可能會有所提升。例如下面的例子中,使用 IN() 代替連線查詢,可以讓 MySQL 按照 ID 順序進行查詢,這可能比隨機的連線要更高效。

SELECT * FROM tab
JOIN tag_post ON tag_post.tag_id=tag.id
JOIN post ON tag_post.post_id=post.id
WHERE tag.tag='mysql';

SELECT * FROM tag WHERE tag='mysql';
SELECT * FROM tag_post WHERE tag_id=1234;
SELECT * FROM post WHERE post.id IN (123,456,567,9098,8904);

24.分析查詢語句

通過對查詢語句的分析,可以瞭解查詢語句執行的情況,找出查詢語句執行的瓶頸,從而優化查詢語句。mysql中提供了EXPLAIN語句和DESCRIBE語句,用來分析查詢語句。
EXPLAIN語句的基本語法如下:
EXPLAIN [EXTENDED] SELECT select_options;
使用EXTENED關鍵字,EXPLAIN語句將產生附加資訊。select_options是select語句的查詢選項,包括from where子句等等。
執行該語句,可以分析EXPLAIN後面的select語句的執行情況,並且能夠分析出所查詢的表的一些特徵。
例如:EXPLAIN SELECT * FROM user;
查詢結果進行解釋說明:
a、id:select識別符,這是select的查詢序列號。
b、select_type:標識select語句的型別。
它可以是以下幾種取值:
b1、SIMPLE(simple)表示簡單查詢,其中不包括連線查詢和子查詢。
b2、PRIMARY(primary)表示主查詢,或者是最外層的查詢語句。
b3、UNION(union)表示連線查詢的第2個或者後面的查詢語句。
b4、DEPENDENT UNION(dependent union)連線查詢中的第2個或者後面的select語句。取決於外面的查詢。
b5、UNION RESULT(union result)連線查詢的結果。
b6、SUBQUERY(subquery)子查詢的第1個select語句。
b7、DEPENDENT SUBQUERY(dependent subquery)子查詢的第1個select,取決於外面的查詢。
b8、DERIVED(derived)匯出表的SELECT(FROM子句的子查詢)。
c、table:表示查詢的表。
d、type:表示表的連線型別。
下面按照從最佳型別到最差型別的順序給出各種連線型別。
d1、system,該表是僅有一行的系統表。這是const連線型別的一個特例。
d2、const,資料表最多隻有一個匹配行,它將在查詢開始時被讀取,並在餘下的查詢優化中作為常數對待。const表查詢速度很快,因為它們唯讀一次。const用於使用常數值比較primary key或者unique索引的所有部分的場合。
例如:EXPLAIN SELECT * FROM user WHERE id=1;
d3、eq_ref,對於每個來自前面的表的行組合,從該表中讀取一行。當一個索引的所有部分都在查詢中使用並且索引是UNIQUE或者PRIMARY KEY時候,即可使用這種型別。eq_ref可以用於使用「=」操作符比較帶索引的列。比較值可以為常數或者一個在該表前面所讀取的表的列的表示式。
例如:EXPLAIN SELECT * FROM user,db_company WHERE user.company_id = db_company.id;
d4、ref對於來自前面的表的任意行組合,將從該表中讀取所有匹配的行。這種型別用於所以既不是UNION也不是primaey key的情況,或者查詢中使用了索引列的左子集,即索引中左邊的部分組合。ref可以用於使用=或者<=>操作符的帶索引的列。
d5、ref_or_null,該連線型別如果ref,但是如果新增了mysql可以專門搜尋包含null值的行,在解決子查詢中經常使用該連線型別的優化。
d6、index_merge,該連線型別表示使用了索引合併優化方法。在這種情況下,key列包含了使用的索引的清單,key_len包含了使用的索引的最長的關鍵元素。
d7、unique_subquery,該型別替換了下面形式的in子查詢的ref。是一個索引查詢函數,可以完全替代子查詢,效率更高。
d8、index_subquery,該連線型別類似於unique_subquery,可以替換in子查詢,但是隻適合下列形式的子查詢中非唯一索引。
d9、range,只檢索給定範圍的行,使用一個索引來選擇行。key列顯示使用了那個索引。key_len包含所使用索引的最長關鍵元素。當使用=,<>,>,>=,<,<=,is null,<=>,between或者in操作符,用常數比較關鍵字列時,型別為range。
d10、index,該連線型別與all相同,除了只掃描索引樹。著通常比all快,引文索引問價通常比資料檔案小。
d11、all,對於前面的表的任意行組合,進行完整的表掃描。如果表是第一個沒有標記const的表,這樣不好,並且在其他情況下很差。通常可以增加更多的索引來避免使用all連線。
e、possible_keys:possible_keys列指出mysql能使用那個索引在該表中找到行。如果該列是null,則沒有相關的索引。在這種情況下,可以通過檢查where子句看它是否引起某些列或者適合索引的列來提高查詢效能。如果是這樣,可以建立適合的索引來提高查詢的效能。
f、key:表示查詢實際使用到的索引,如果沒有選擇索引,該列的值是null,要想強制mysql使用或者忽視possible_key列中的索引,在查詢中使用force index、use index或者ignore index
g、key_len:表示mysql選擇索引欄位按照位元組計算的長度,如果健是null,則長度為null。注意通過key_len值可以確定mysql將實際使用一個多列索引中的幾個欄位。
h、ref:表示使用那個列或者常數或者索引一起來查詢記錄。
i、rows:顯示mysql在表中進行查詢必須檢查的行數。
j、Extra:該列mysql在處理查詢時的詳細資訊。

推薦學習:

以上就是超詳細彙總mysql優化實踐技巧的詳細內容,更多請關注TW511.COM其它相關文章!