1. 程式人生 > >mysql 效能優化方法彙總

mysql 效能優化方法彙總

0、加索引

索引建立規則:

1.索引的欄位必須是經常作為查詢條件的欄位;
2.如果索引多個欄位,第一個欄位要是經常作為查詢條件的。如果只有第二個欄位作為查詢條件,這個索引不會起到作用;
3.索引的欄位必須有足夠的區分度;
4.Mysql 對於長欄位支援字首索引;

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

當你查詢表的有些時候,你已經知道結果只會有一條結果,但因為你可能需要去fetch遊標,或是你也許會去檢查返回的記錄數。

在這種情況下,加上 LIMIT 1 可以增加效能。這樣一樣,MySQL資料庫引擎會在找到一條資料後停止搜尋,而不是繼續往後查少下一條符合記錄的資料。

下面的示例,只是為了找一下是否有“中國”的使用者,很明顯,後面的會比前面的更有效率。(請注意,第一條中是Select *,第二條是Select 1)

1.//沒有效率的:
2.$r = mysql_query("SELECT * FROM user WHERE country = 'China'");
3.if (mysql_num_rows($r) > 0) {
4.    //...
5.}
6.// 有效率的:
7.$r = mysql_query("SELECT 1 FROM user WHERE country = 'China' LIMIT 1");
8.if (mysql_num_rows($r) > 0) {
9.    //...
10.}

2、 千萬不要 ORDER BY RAND()

想打亂返回的資料行?隨機挑一個數據?真不知道誰發明了這種用法,但很多新手很喜歡這樣用。但你確不瞭解這樣做有多麼可怕的效能問題。

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

下面的示例是隨機挑一條記錄

1.// 千萬不要這樣做:
2.$r = mysql_query("SELECT username FROM
user ORDER BY RAND() LIMIT 1"); 3. // 這要會更好: 4.$r = mysql_query("SELECT count(*) FROM user"); 5.$d = mysql_fetch_row($r); 6.$rand = mt_rand(0,$d[0] - 1); 7.$r = mysql_query("SELECT username FROM user LIMIT $rand, 1");

3、 避免 SELECT *

從資料庫裡讀出越多的資料,那麼查詢就會變得越慢。並且,如果你的資料庫伺服器和WEB伺服器是兩臺獨立的伺服器的話,這還會增加網路傳輸的負載。

所以,你應該養成一個需要什麼就取什麼的好的習慣。

// 不推薦
1.$r = mysql_query("SELECT * FROM user WHERE user_id = 1");
2.$d = mysql_fetch_assoc($r);
3.echo "Welcome {$d['username']}";
4.// 推薦
5.$r = mysql_query("SELECT username FROM user WHERE user_id = 1");
6.$d = mysql_fetch_assoc($r);
7.echo "Welcome {$d['username']}";

4、永遠為每張表設定一個ID

我們應該為資料庫裡的每張表都設定一個ID做為其主鍵,而且最好的是一個INT型的(推薦使用UNSIGNED),並設定上自動增加的AUTO_INCREMENT標誌。

就算是你 users 表有一個主鍵叫 “email”的欄位,你也別讓它成為主鍵。使用 VARCHAR 型別來當主鍵會使用得效能下降。另外,在你的程式中,你應該使用表的ID來構造你的資料結構。

而且,在MySQL資料引擎下,還有一些操作需要使用主鍵,在這些情況下,主鍵的效能和設定變得非常重要,比如,叢集,分割槽……

在這裡,只有一個情況是例外,那就是“關聯表”的“外來鍵”,也就是說,這個表的主鍵,通過若干個別的表的主鍵構成。我們把這個情況叫做“外來鍵”。比 如:有一個“學生表”有學生的ID,有一個“課程表”有課程ID,那麼,“成績表”就是“關聯表”了,其關聯了學生表和課程表,在成績表中,學生ID和課 程ID叫“外來鍵”其共同組成主鍵。

5、儘可能的使用 NOT NULL

除非你有一個很特別的原因去使用 NULL 值,你應該總是讓你的欄位保持 NOT NULL。這看起來好像有點爭議,請往下看。

首先,問問你自己“Empty”和“NULL”有多大的區別(如果是INT,那就是0和NULL)?如果你覺得它們之間沒有什麼區別,那麼你就不要使用NULL。(你知道嗎?在 Oracle 裡,NULL 和 Empty 的字串是一樣的!)

不要以為 NULL 不需要空間,其需要額外的空間,並且,在你進行比較的時候,你的程式會更復雜。 當然,這裡並不是說你就不能使用NULL了,現實情況是很複雜的,依然會有些情況下,你需要使用NULL值。

下面摘自MySQL自己的文件:
NULL columns require additional space in the row to record whether their values are NULL. For MyISAM tables, each NULL column takes one bit extra, rounded up to the nearest byte.”

6、選擇正確的儲存引擎

在 MySQL 中有兩個儲存引擎 MyISAM 和 InnoDB,每個引擎都有利有弊。酷殼以前文章《MySQL: InnoDB 還是 MyISAM?》討論和這個事情。

MyISAM 適合於一些需要大量查詢的應用,但其對於有大量寫操作並不是很好。甚至你只是需要update一個欄位,整個表都會被鎖起來,而別的程序,就算是讀程序都 無法操作直到讀操作完成。另外,MyISAM 對於 SELECT COUNT(*) 這類的計算是超快無比的。

InnoDB 的趨勢會是一個非常複雜的儲存引擎,對於一些小的應用,它會比 MyISAM 還慢。他是它支援“行鎖” ,於是在寫操作比較多的時候,會更優秀。並且,他還支援更多的高階應用,比如:事務。

下面是MySQL的手冊
target=”_blank”MyISAM Storage Engine
InnoDB Storage Engine

7、對錶進行水平劃分

   如果一個表的記錄數太多了,比如上千萬條,而且需要經常檢索,那麼我們就有必要化整為零了。如果我拆成100個表,那麼每個表只有10萬條記錄。當然這 需要資料在邏輯上可以劃分。一個好的劃分依據,有利於程式的簡單實現,也可以充分利用水平分表的優勢。比如系統介面上只提供按月查詢的功能,那麼把表按月 拆分成12個,每個查詢只查詢一個表就夠了。如果非要按照地域來分,即使把表拆的再小,查詢還是要聯合所有表來查,還不如不拆了。所以一個好的拆分依據是 最重要的。
   水平分表,能夠降低單表的資料量,一定程度上可以緩解查詢效能瓶頸。但本質上這些表還儲存在同一個庫中,所以庫級別還是會有IO瓶頸。所以,一般不建議採用這種做法。
   
8、對錶進行垂直劃分

  有些表記錄數並不多,可能也就2、3萬條,但是欄位卻很長,表佔用空間很大,檢索表時需要執行大量I/O,嚴重降低了效能。這個時候需要把大的欄位拆分到另一個表,並且該表與原表是一對一的關係。(降低了每張表的檔案大小)
  例項:文章資訊表就可以拆分成兩張表,一張記錄文章資訊如作者、欄目、發表日期、關鍵詞等,另一張表記錄 文章內容,摘要等。利用id進行對應。因為在大多數情況下只需要搜尋第一張表即可,只有在文章展示頁才需要訪問兩張表,如此可以提高搜尋效能!

9、mysql主從同步,讀寫分離

詳見:
http://blog.csdn.net/u013372487/article/details/51658692

10、使用INNER JOIN 而不是WHERE來建立連線(多表查詢)

一些SQL開發人員更喜歡使用WHERE來做join,比如:

SELECT Customers.CustomerID, Customers.Name, Sales.LastSaleDate
FROM Customers, Sales WHERE Customers.CustomerID = Sales.CustomerID

這個型別where實際上建立時笛卡爾連線。 在笛卡爾連線中,所有可能的組合都會被創建出來。在上面的例子中,如果有1000顧客和1000條銷售記錄,這個查詢會先產生1000000個結果,然後通過正確的 CustomerID過濾出1000條記錄。 這是一種低效利用資料庫資源,資料庫多做100倍的工作。 在大型資料庫中,笛卡爾連線是一個大問題,對兩個大表的笛卡爾積會建立數10億或萬億的記錄。
為了避免建立笛卡爾積,應該使用INNER JOIN :

1.SELECT Customers.CustomerID, Customers.Name, Sales.LastSaleDate
2.FROM Customers INNER JOIN Sales ON Customers.CustomerID = Sales.CustomerID

這樣資料庫就只產生等於CustomerID 的1000條目標結果。
有些資料庫系統會識別出 WHERE連線並自動轉換為 INNER JOIN。在這些資料庫系統中,WHERE 連線與INNER JOIN 就沒有效能差異。但是, INNER JOIN 是所有資料庫都能識別的,因此DBA會建議在你的環境中使用它。

11、水平分庫分表

平分庫分表與上面講到的水平分表的思想相同,唯一不同的就是將這些拆分出來的表儲存在不同的資料中。這也是很多大型網際網路公司所選擇的做法。如下圖:
這裡寫圖片描述

某種意義上來講,有些系統中使用的“冷熱資料分離”(將一些使用較少的歷史資料遷移到其他的資料庫中。而在業務功能上,通常預設只提供熱點資料的查詢),也是類似的實踐。在高併發和海量資料的場景下,分庫分表能夠有效緩解單機和單庫的效能瓶頸和壓力,突破IO、連線數、硬體資源的瓶頸。當然,投入的硬體成本也會更高。同時,這也會帶來一些複雜的技術問題和挑戰(例如:跨分片的複雜查詢,跨分片事務等)

12、垂直分庫

垂直分庫在“微服務”盛行的今天已經非常普及了。基本的思路就是按照業務模組來劃分出不同的資料庫,而不是像早期一樣將所有的資料表都放到同一個資料庫中。如下圖:
這裡寫圖片描述