根據《高性能MySQL》整理一些數據庫優化建議

根據《高性能MySQL》一書,整理一些數據庫優化建議。

1.爲字段選擇合適的數據類型

概括來說,儘可能保持任何東西小而簡單總是好的。MySQL喜歡簡單,需要使用數據庫的人
應該也同樣會喜歡簡單的原則.

  1. 儘量避免過度設計,例如會導致極其複雜查詢的schema設計,或者有很多列的表設計(很多的意思是介於有點多和非常多之間).
  2. 使用小而簡單的合適數據類型,除非真實數據模型中有確切的需要,否則應該儘可能地避免使用NULL值。
  3. 儘量使用相同的數據類型存儲相似或相關的值,尤其是要在關聯條件中使用的列。
  4. 注意可變長字符串,其在臨時表和排序時可能導致悲觀的按最大長度分配內存。
  5. 儘量使用整型定義標識列。
  6. 避免使用MySQL已經遺棄的特性,例如指定浮點數的精度,或者整數的顯示寬度。
  7. 小心使用ENUM和SETO雖然它們用起來很方便,但是不要濫用,否則有時候會變成陷阱。最好避免使用BIT。

2.創建高性能的索引

在選擇索引和編寫利用這些索引的查詢時,有如下三個原則始終需要記住:

  • 單行訪問是很慢的。特別是在機械硬盤存儲中(SSD的隨機I/O要快很多,不過這一點仍然成立)。如果服務器從存儲中讀取一個數據塊只是爲了獲取其中一行,那麼就浪費了很多工作。最好讀取的塊中能包含儘可能多所需要的行。使用索引可以創建位置引,用以提升效率。
  • 按順序訪問範圍數據是很快的,這有兩個原因。第一,順序I/O不需要多次磁盤尋道,所以比隨機I/O要快很多(特別是對機械硬盤)。第二,如果服務器能夠按需要順序讀取數據,那麼就不再需要額外的排序操作,並且GROUPBY查詢也無須再做排序和將行按組進行聚合計算了。
  • 索引覆蓋查詢是很快的。如果一個索引包含了查詢需要的所有列,那麼存儲引擎就不需要再回表查找行。這避免了大量的單行訪問,而上面的第1點已經寫明單行訪問是很慢的。
  1. 在字段長度較大的字段上建立索引時,根據選擇性原則,選取合適的長度建立前綴索引。
  2. 在where語句後面進行and字段連接時,考慮建立多列索引,併合理安排多列索引的順序,以便利用索引掃描來排序(既能查詢行又能對結果排序)。
  3. 在合適的字段上建立聚簇索引,一般的主鍵就是聚簇索引,數據行和索引都保存在B-Tree的葉子節點,這樣查詢到索引就找到了數據,不用再回表查詢。
  4. 使用“覆蓋索引”。所謂覆蓋索引就是,所有需要查詢的字段都是索引字段。例如在age上建立B-Tree索引,查詢SELECT age from user WHERE age = 13;由於age索引和數據行都被保存在B-Tree葉子節點,在獲取索引的時候也直接獲得了數據,不用回表查詢,所以查詢速度很快。
  5. 合理對待冗餘索引,對於重複索引則必須刪除。
  6. 在使用索引字段作爲條件時,如果該索引是多列索引,那麼必須使用到該索引中的第一個字段作爲條件時才能保證系統使用該索引,否則該索引將不會被使 用,並且應儘可能的讓字段順序與索引順序相一致。(最左匹配原則)。
  7. 索引並不是越多越好,索引固然可以提高相應的 select 的效率,但同時也降低了 insert 及 update 的效率,因爲 insert 或 update 時有可能會重建索引,所以怎樣建索引需要慎重考慮,視具體情況而定。一個表的索引數最好不要超過6個,若太多則應考慮一些不常使用到的列上建的索引是否有必要。
  8. 應儘可能的避免更新聚簇索引數據列,因爲聚簇索引數據列的順序就是表記錄的物理存儲順序,一旦該列值改變將導致整個表記錄的順序的調整,會耗費相當大的資源。若應用系統需要頻繁更新聚簇索引索引數據列,那麼需要考慮是否應將該索引建爲聚簇索引索引。

3.優化查詢語句

避免向數據庫請求不需要的數據

  1. 避免查詢不需要的記錄,在查詢後面加上LIMIT
  2. 避免select *
  3. 避免重複查詢相同的數據,可以將相同的數據進行緩存

避免數據庫掃描過多的數據行而只返回少數的行

  1. 使用索引覆蓋掃描,把所有需要查詢的列都放到索引中,這樣存儲引擎無須回表獲取對應行就可以返回結果了。
  2. 優化LIMIT,例如" LIMIT 10000,20“會掃描10020條數據只返回20條。可以使用查詢和BETWEEN AND優化。
  3. 改變庫表結構。例如使用單獨的彙總表。

避免執行耗時的大語句

因爲一個大的語句一次性完成的話,則可能需要一次鎖住很多數據、佔滿整個事務日誌、耗盡系統資源、阻塞很多小的但重要的查詢。例如,一次性刪除10w條記錄。可以使用LIMIT切分查詢,一次刪除1000條。

優化關聯查詢:

  1. 確保ON或者USING子句中的列上有索引。在創建索引的時候就要考慮到關聯的順序。當表A和表B用列c關聯的時候,如果優化器的關聯順序是B、A,那麼就不需要在B表的對應列上創建索引。沒有用到的索引只會帶來額外的負擔。一般來說,除非有其他理由。否則只需要在關聯順序中的第二個表的相應列上創建索引。原因:MySQL對任何關聯都執行嵌套循環關聯操作。
  2. 確保任何的GROUP BY 和ORDER BY中的表達式只涉及到一個表中的列,這樣MySQL纔可能使用索引來優化這個過程。
  3. 有時候,可以將一個複雜的關聯查詢拆分爲多個簡單查詢。

 

  • 簡單查詢的結果可以緩存起來,提高緩存的效率。
  • 執行單個簡單查詢可以減少鎖的競爭。
  • 在應用層做關聯,可以更容易對數據庫進行拆分,更容易做到高性能和可擴展。
  • 查詢本身效率也可能會有所提升。這個例子中,用IN()代替關聯查詢,可以讓MySQL按照ID順序進行查詢,這可能比隨機的關聯要更高效。
  • 可以減少冗餘記錄的查詢。在應用層做關聯查詢,意味着對於某條記錄應用只需要查詢一次,而在數據庫中做關聯查詢,則可能需要重複地訪問一部分數據。從這點看,這樣的重構還可能會減少網絡和內存的消耗。

優化子查詢:

在MySQL 5.6及以上版本中,不再建議使用關聯查詢代替子查詢,高版本的MySQL對子查詢進行了優化。MySQL 5.6 版本以前會建議避免使用IN+子查詢,而使用下面兩種方式代替。高版本對其進行了優化,可以使用IN+子查詢的方式。

mysql > SELECT  *  FROM sakila.film
         ->WHERE film_id IN(
         ->        SELECT film_id FROM sakila.filn_actor WHERE actor_id = 1);

優化一:

       SELECT  *  FROM sakila.film
       WHERE EXISTS(
            SELECT  *  FROM  sakila.film  actor  WHERE  actorid = 1
            AND film_actor.film_id  =  film.film_id);

優化二:

        SELECT  film.*  FROM   sakila.film
        INNER  JOIN  sakila.film_actor USING(film_id)
        WHERE  actor_id = 1;

優化UNION查詢:

除非確實需要服務器消除重複好行,否則一定要使用UNION ALL,這一點很重要 。 如果沒有ALL關鍵字,MySQL會給臨時表加上DISTINCT選項,這會導致對整個臨時表的數據做唯一性檢查。這樣做的代價非常高。即使有ALL關鍵字,MySQL仍然會使用臨時表存儲結果。事實上, MySQL總是將結果放人臨時表,然後再讀出,再返回給客戶端。雖然很多時候這樣做是沒有以要的(例如,MySQL可以直接把這些結果返回給客戶端 )。

自定義變量:(限制比較大,謹慎使用)

用戶自定義變量是一個用來存儲內容的臨時容器,在連接MySQL的整個過程中都存在。
可以使用下面的SET和SELECT語句來定義它們:
mysql> SET @one              :=1
mysql> SET @min_actor    :=or:=(SELECT MIN(actor_dd) FROM sakila.actor);
mysql> SET @last_week    :=CURRENT_DATE-INTERVAL 1 WEEK;

然後可以在任何可以使用表達式的地方使用這些自定義變量:
mysql> SELECT ...  WHERE col <= @last_week;

其他建議:

  1. 應儘量避免在 where 子句中使用!=、<>、in、not in、between and 等範圍條件查詢,否則將導致存儲引擎放棄使用索引而進行全表掃描。
  2. 避免使用前置%查詢,會導致全表掃描。
  3. 儘量避免在 where 子句中使用 or 來連接條件,如果or的字段上有一個字段沒有建立索引,將導致引擎放棄使用索引而進行全表掃描。如:                                                                                                                                                                  SELECT id from user WHERE age = 13 or phone = '13167001221';                                                                                    若在age和phone兩個字段其中一個字段上沒有索引,則不會使用索引。必須都有索引才使用索引。可以這樣查詢:SELECT id from user WHERE age = 13union allSELECT id from user WHERE phone = '13167001221';
  4. 應儘量避免在where子句中對字段進行函數操作及表達式操作,這將導致引擎放棄使用索引而進行全表掃描。如:    SELECT * from user WHERE age = 3;使用索引                                                                                                                  SELECT * from user WHERE age/2 = 3; 不使用索引
  5. 儘量使用數字型字段,若只含數值信息的字段儘量不要設計爲字符型,這會降低查詢和連接的性能,並會增加存儲開銷。這是因爲引擎在處理查詢和連接時會 逐個比較字符串中每一個字符,而對於數字型而言只需要比較一次就夠了。
  6. 儘可能的使用 varchar/nvarchar (必須指定長度)代替 char/nchar ,因爲首先變長字段存儲空間小,可以節省存儲空間,其次對於查詢來說,在一個相對較小的字段內搜索效率顯然要高些。
  7. 避免頻繁創建和刪除臨時表,以減少系統表資源的消耗。
  8. 在新建臨時表時,如果一次性插入數據量很大,那麼可以使用 select into 代替 create table,避免造成大量 log ,以提高速度;如果數據量不大,爲了緩和系統表的資源,應先create table,然後insert。
  9. 如果使用到了臨時表,在存儲過程的最後務必將所有的臨時表顯式刪除,先 truncate table ,然後 drop table ,這樣可以避免系統表的較長時間鎖定。
  10. 儘量避免大事務操作,提高系統併發能力。

附:MySQL怎樣執行關聯查詢

當前MySQL關聯執行的策略很簡單:MySQL對任何關聯都執行嵌套循環關聯操作,即MySQL先在一個表中循環取出單條數據,
然後再嵌套循環到下一個表中尋找匹配的行,依次下去,直到找到所有表中匹配的行爲止。然後根據各個表匹配的行,返回查詢中需要的各個列。MySQL會嘗試在最後一個關聯表中找到所有匹配的行,如果最後一個關聯表無法找到更多的行以後,MySQL返回到上一層次關聯表,看是否能夠找到更多的匹配記錄,依此類推迭代執行。按照這樣的方式查找第一個表記錄,再嵌套查詢下一個關聯表,然後回溯到上一個表,在MySQL中是通過嵌套循環的方式實現一一正如其名“嵌套循環關聯"。
請看下面的例子中的簡單查詢:

假設MySQL按照查詢中的表順序進行關聯操作,我們則可以用下面的僞代碼表示MySQL將如何完成這個查詢:

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章