多項改善MySQL效能的方式(一)

  1. MySQL內含Cache功能,以加快多次重複執行的Queries,所以如果不必要的話我們要避免使用一些經常令Query script 改變的內建function,例子如下:
    // cache 無法發揮效用 ,因為每次的Query script也不相同
    $r = mysql_query("SELECT username FROM user WHERE signup_date >= CURDATE()");
    // 在當天的一整天裏cache成功地發揮效用,因為Query script在這天是相同的
    $today = date("Y-m-d");
    $r = mysql_query("SELECT username FROM user WHERE signup_date >= '$today'");

    所以我們要避免使用CURDATE(),NOW() 和 RAND()這些內建function.
  2. 在SELECT之前加上EXPLAIN,將會得到SELECT後的詳細資料。
  3. 當你知道你SELECT的結果是唯一的(也就是說只會有一行的資料),請你在Query script 的最後加上LIMIT 1,這樣可以通知MySQL在取得一行資料後立即停止搜索。
  4. 請你將經常要進行搜尋的欄位設為INDEX,這樣可以大大加快搜尋效能。
  5. 如果你要將兩個Table進行Join,那麼兩個table中用於對應的欄位,請盡量使用相同的column type.
  6. 請不要使用MySQL的內建RAND() function,這個function將會大大地減低執行效能,因為它將會對每一行資料進行操作,而你可能最後只需要取得一行的資料。
    //避免使用以下方式
    $r = mysql_query("SELECT username FROM user ORDER BY RAND() LIMIT 1");
    //請使用多一點程式碼去改善執行的效能
    $r = mysql_query("SELECT count(*) FROM user");
    $d = mysql_fetch_row($r);
    $rand = mt_rand(0,$d[0] - 1);
    $r = mysql_query("SELECT username FROM user LIMIT $rand, 1");

  7. 避免使用SELECT *,如果知道要使用多少個欄位的資料,那麼請盡量只提取那些欄位的資料,不要將整行的資料取出。
  8. 如果可以的話,請將每個Table也加上一個int unsigned primary key 的欄位,請勿將varchar或char等等的欄位設為primary key,這樣會減低搜尋效能。
  9. 如果可以的話,請將全部欄位都設為not null,如果將欄位設為null,那麼跟not null 而是空白string並沒有分別。以下是mysql doc的解說:
    “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.”
  10. 用於儲存IP ADDRESS的欄位,請設為UNSIGNED INT,這樣可以節省TABLE空間,請參考以上方式:
    $r = "UPDATE users SET ip = INET_ATON('{$_SERVER['REMOTE_ADDR']}') WHERE user_id = $user_id";

    你也可以使用php的ip2long()和long2ip()進行轉換。
  11. 固定長度的column type將會加快搜尋效能。(非固定長度的type 有VARCHAR, TEXT, BLOB)
  12. 適當地分割資料表資料,需要考慮的有以下情況:
    1.在user的資料表中,有一個address欄位,在一般的情況下並不會經常使用到它,所以可以將address欄位分割到其他的資料表,以減少資料表的大小,這樣也可以加快查詢的速度。
    2.在user資料表中,有一個last_login的欄位,在每次的登入也會進行更新,在這個情況下會影響到MySQL的cache功能,所以你可以將經常要改動的欄位抽走,減少對table的影響。
  13. 分割超大量的delete跟insert query,如果你要刪除的欄位或是想加入的欄位非常大量,這樣將會霸佔Mysql的connection,同時也會鎖定整個Table,大大地影響到其他需要使用該table的部份,由其是超過30秒的執行。所以如果真的需要大量執行這類的動作,請將該動作分割,例如:
    while (1) {
         mysql_query("DELETE FROM logs WHERE log_date <= '2009-10-01' LIMIT 10000");
         if (mysql_affected_rows() == 0) { 
              break;
         }
         usleep(50000);
    }

  14. 超小的column type將會令查詢越快。

1 則留言:

匿名 提到...

Good Job !
有 part 2 嗎?