MySQL 慢 SQL & 優化方案 explain 命令詳解

https://www.cnblogs.com/juno3550/p/14887672.html#label3.2

1. 慢 SQL 的危害

2. 數據庫架構 & SQL 執行過程

3. 存儲引擎和索引的那些事兒

4. 慢 SQL 解決之道

 

 

1. 慢 SQL 的危害

慢 SQL,就是跑得很慢的 SQL 語句,你可能會問慢 SQL 會有啥問題嗎?

試想一個場景:

大白和小黑端午出去玩,機票太貴於是買了高鐵,火車站的人真是烏央烏央的。

馬上檢票了,大白和小黑準備去廁所清理下庫存,坑位不多,排隊的人還真不少。

小黑髮現其中有 3 個坑的乘客賊慢,其他 2 個坑位換了好幾波人,這 3 位坑主就是不出來。

等在外面的大夥,心裏很是不爽,長期佔用公共資源,後面的人沒法用。

小黑苦笑道:這不就是廁所版的慢 SQL 嘛!

這是實際生活中的例子,換到 MySQL 服務器也是一樣的,畢竟科技源自生活嘛。

MySQL 服務器的資源(CPU、IO、內存等)是有限的,尤其在高併發場景下需要快速處理掉請求,否則一旦出現慢 SQL 就會阻塞掉很多正常的請求,造成大面積的失敗/超時等。

  

2. 數據庫架構 & SQL 執行過程

 

 如上圖所示,MySQL 邏輯架構圖主要分三層:

  1. 第一層負責連接處理、授權認證等。
  2. 第二層負責編譯並優化 SQL。
  3. 第三層是存儲引擎。

SQL 執行過程:

  1. 客戶端發送一條 SQL 語句給服務端,服務端的連接器先進行賬號/密碼、權限等驗證,若有異常則直接拒絕請求。
  2. 服務端先查詢緩存(MySQL8.0 已取消查詢緩存),如果 SQL 語句命中了緩存,則返回緩存中的結果,否則繼續處理。
  3. 服務端對 SQL 語句進行詞法和語法分析,提取 SQL 中 select 等關鍵字,來檢查 SQL 語句的合法性。
  4. 服務端通過優化器對之前生成的解析樹進行優化處理,生成最優的物理執行計劃
  5. 將生成的物理執行計劃調用存儲引擎的相關接口,進行數據查詢和處理。
  6. 處理完成後將結果返回客戶端。

 

俗話說“條條大路通羅馬”,優化器的作用就是找到這麼多路中最優的那一條。

存儲引擎更是決定 SQL 執行的核心組件,適當瞭解其中原理十分有益。

 

3. 存儲引擎和索引的那些事兒

3.1 存儲引擎

InnoDB 存儲引擎(Storage Engine)是 MySQL 默認之選,所以非常典型。

儲引擎的主要作用是進行數據的存取和檢索,也是真正執行 SQL 語句的組件。

InnoDB 的整體架構分爲兩個部分:內存架構和磁盤架構,如圖:

存儲引擎的內容非常多,並不是一篇文章能說清楚的,本文不過多展開,我們在此只需要瞭解內存架構和磁盤架構的大致組成即可。

InnoDB 引擎是面向行存儲的,數據都是存儲在磁盤的數據頁中,數據頁裏面按照固定的行格式存儲着每一行數據。

行格式主要分爲四種類型:Compact、Redundant、Dynamic 和 Compressed,默認爲 Compact 格式。

操作系統

1)局部性原理

  • 時間局部性:之前被訪問過的數據很有可能被再次訪問。
  • 空間局部性:數據和程序都有聚集成羣的傾向。

2)磁盤預讀機制

當計算機訪問一個數據時,不僅會加載當前數據所在的數據頁,還會將當前數據頁相鄰的數據頁一同加載到內存,磁盤預讀的長度一般爲頁的整倍數,從而有效降低磁盤 I/O 的次數(如果要讀取的數據量超過一頁的大小,就會觸發多次磁盤 I/O 操作)。

磁盤和內存的交互

MySQL 中磁盤的數據需要被交換到內存,才能完成一次 SQL 交互,大致如圖:

  • 扇區是硬盤讀寫的基本單位,通常情況下每個扇區的大小是 512B。
  • 磁盤塊是操作系統(文件系統)讀寫數據的最小單位,相鄰的扇區組合在一起形成一個塊,一般是 4KB。
  • 是內存的最小存儲單位,頁的大小通常爲磁盤塊大小的 2n 倍。
  • InnoDB 的頁的默認大小是 16KB,是數倍個操作系統的頁。

隨機磁盤 I/O

MySQL 的數據是一行行存儲在磁盤上的,並且這些數據並非物理連續地存儲,這樣的話要查找數據就無法避免隨機在磁盤上讀取和寫入數據。

對於 MySQL 來說,當出現大量磁盤隨機 I/O 時,大部分時間都被浪費到尋道上,磁盤呼嚕呼嚕轉,就是傳輸不了多少數據。

一次磁盤訪問由三個動作組成:

  • 尋道(Seek Time):磁頭移動定位到指定磁道。
  • 旋轉(Rotational Latency):等待指定扇區從磁頭下旋轉經過。
  • 數據傳輸(Transfer Time):數據在磁盤與內存之間的實際傳輸。

對於存儲引擎來說,如何有效降低隨機 I/O 是個非常重要的問題。 

 

3.2 索引 

詳見《MySQL 索引》。 

 

4. 慢 SQL 解決之道

在遇到慢 SQL 時,不能簡單的把原因歸結爲 SQL 編寫問題(雖然這是最常見的因素),實際上導致慢 SQL 有很多因素,大致如下:

  1. 索引設計問題
  2. SQL 編寫問題
  3. 表結構(類型、長度等)設計問題
  4. 併發對 IO/CPU 資源爭用
  5. 服務器硬件
  6. MySQL 本身的 Bug

接下來將從以下幾個方面分析慢 SQL 的解決之道:

 

  

4.1 優化分析流程 

1)瞭解各種 SQL 的執行效率

show status like 'Com_%';  -- 瞭解各種SQL的執行頻率
  • Com_select    | 1   執行 select 操作的次數,一次查詢只累加 1。
  • Com_insert     | 0   執行 insert 操作的次數,對於批量插入的 insert,只累加一次。
  • Com_update   | 0   執行 update 操作的次數。
  • Com_delete    | 0   執行 delete 操作的次數。

上述參數對 所有存儲引擎 的表操作都會進行累計。

下面這幾個參數只是針對 InnoDB 存儲引擎的,累加的算法也略有不同。

show status like 'Innodb_rows_%';
  • Innodb_rows_deleted     | 1 |          執行 delete 操作刪除的行數。
  • Innodb_rows_inserted    | 50 |        執行 insert 操作插入的行數。
  • Innodb_rows_read          | 168 |      執行 select 查詢返回的行數。
  • Innodb_rows_updated    | 0 |          執行 updat 操作更新的行數。

通過以上幾個參數,我們可以瞭解當前數據庫的應用是以插入更新爲主還是以查詢操作爲主,以及各種類型的 SQL 大致的執行比例是多少。

事務型的應用,通過 Com_commit 和 Com_rollback 可以瞭解事務提交和回滾的情況,對於回滾操作非常頻繁的數據庫,可能意味着應用編寫存在問題

2)定位慢查詢

可以通過以下兩種方式定位執行效率較低的 SQL 語句(慢查詢的統計通常由運維定期統計):

  1. 通過慢查詢日誌定位那些執行效率較低的 SQL 語句,具體可參見《MySQL 日誌》的慢查詢日誌部分。
  2. 慢查詢日誌在查詢結束以後才紀錄,所以在應用反映執行效率出現問題時,查詢慢查詢日誌並不能定位問題。這時可以使用 show processlist 命令查看當前 MySQL 在進行的線程,包括線程的狀態、是否鎖表等,可以實時地查看 SQL 的執行情況,同時對一些鎖表操作進行優化。

show processlist 命令詳解

show processlist 命令只列出前 100 條正在運行的線程信息,如果想全列出需要使用 show full processlist。也可以使用 mysqladmin processlist 語句得到此信息。

除非有 SUPER 權限,可以看到所有線程。否則,只能看到自己的線程(也就是,與您正在使用的 MySQL 賬戶相關的線程)。

本語句會報告 TCP/IP 連接的主機名稱(採用 host_name:client_port 格式),以方便地判定哪個客戶端正在做什麼。

如果得到了“too many connections”錯誤信息,並且想要了解正在發生的情況,本語句是非常有用的。MySQL保留一個額外的連接,讓擁有 SUPER 權限的賬戶使用,以確保管理員能夠隨時連接和檢查系統(假設沒有把此權限給予所有的用戶)。

複製代碼
id       # ID標識,要kill一個語句的時候很有用
use      # 當前連接用戶
host     # 顯示這個連接從哪個ip的哪個端口上發出
db       # 數據庫名
command  # 連接狀態,一般是休眠(sleep),查詢(query),連接(connect),初始化(init)
time     # 連接持續時間,單位是秒
state    # 顯示當前sql語句的狀態
info     # 顯示這個sql語句
複製代碼

該命令中最關鍵的就是 state 列,MySQL 列出的狀態主要有以下幾種:

  • Checking table:正在檢查數據表(這是自動的)。
  • Closing tables:正在將表中修改的數據刷新到磁盤中,同時正在關閉已經用完的表。這是一個很快的操作,如果不是這樣的話,就應該確認磁盤空間是否已經滿了或者磁盤是否正處於重負中。
  • Connect out:複製從服務器正在連接主服務器。
  • Copying to tmp table on disk:由於臨時結果集大於 tmp_table_size,正在將臨時表從內存存儲轉爲磁盤存儲以此節省內存。
  • Creating tmp table:正在創建臨時表以存放部分查詢結果。
  • deleting from main table:服務器正在執行多表刪除中的第一部分,剛刪除第一個表。
  • deleting from reference tables:服務器正在執行多表刪除中的第二部分,正在刪除其他表的記錄。
  • Flushing tables:正在執行FLUSH TABLES,等待其他線程關閉數據表。
  • Killed:發送了一個 kill 請求給某線程,那麼這個線程將會檢查 kill 標誌位,同時會放棄下一個 kill 請求。MySQL 會在每次的主循環中檢查 kill 標誌位,不過有些情況下該線程可能會過一小段才能死掉。如果該線程程被其他線程鎖住了,那麼 kill 請求會在鎖釋放時馬上生效。
  • Locked:被其他查詢鎖住了。
  • Sending data:正在處理 SELECT 查詢的記錄,同時正在把結果發送給客戶端。
  • Sorting for group:正在爲 GROUP BY 做排序。
  • Sorting for order:正在爲 ORDER BY 做排序。
  • Opening tables:這個過程應該會很快,除非受到其他因素的干擾。例如,在執 ALTER TABLE 或 LOCK TABLE 語句行完以前,數據表無法被其他線程打開。正嘗試打開一個表。
  • Removing duplicates:正在執行一個 SELECT DISTINCT 方式的查詢,但是 MySQL 無法在前一個階段優化掉那些重複的記錄。因此,MySQL 需要再次去掉重複的記錄,然後再把結果發送給客戶端。
  • Reopen table:獲得了對一個表的鎖,但是必須在表結構修改之後才能獲得這個鎖。已經釋放鎖,關閉數據表,正嘗試重新打開數據表。
  • Repair by sorting:修復指令正在排序以創建索引。
  • Repair with keycache:修復指令正在利用索引緩存一個一個地創建新索引。它會比 Repair by sorting 慢些。
  • Searching rows for update:正在講符合條件的記錄找出來以備更新。它必須在 UPDATE 要修改相關的記錄之前就完成了。
  • Sleeping:正在等待客戶端發送新請求.:
  • System lock:正在等待取得一個外部的系統鎖。如果當前沒有運行多個 mysqld 服務器同時請求同一個表,那麼可以通過增加 --skip-external-locking 參數來禁止外部系統鎖。
  • Upgrading lock:正在嘗試取得一個鎖表以插入新記錄。
  • Updating:正在搜索匹配的記錄,並且修改它們。
  • User Lock:正在等待 GET_LOCK()。
  • Waiting for tables:該線程得到通知,數據表結構已經被修改了,需要重新打開數據表以取得新的結構。然後,爲了能的重新打開數據表,必須等到所有其他線程關閉這個表。以下幾種情況下會產生這個通知:FLUSH TABLES tbl_name, ALTER TABLE, RENAME TABLE, REPAIR TABLE, ANALYZE TABLE 或 OPTIMIZE TABLE。
  • waiting for handler insert:已經處理完了所有待處理的插入操作,正在等待新的請求。

大部分狀態對應很快的操作,只要有一個線程保持同一個狀態好幾秒鐘,那麼可能是有問題發生了,需要檢查一下。

還有其他的狀態沒在上面中列出來,不過它們大部分只是在查看服務器是否有存在錯誤是才用得着。

3)連接數

當數據庫連接池被佔滿時,如果有新的 SQL 語句要執行,只能排隊等待,等待連接池中的連接被釋放(等待之前的 SQL 語句執行完成)。

如果監控發現數據庫連接池的使用率過高,甚至是經常出現排隊的情況,則需要進行調優。

查看/設置最大連接數

複製代碼
-- 查看最大連接數
mysql> show variables like '%max_connection%';
+-----------------------+-------+
| Variable_name         | Value |
+-----------------------+-------+
| extra_max_connections |       |
| max_connections       | 2512  |
+-----------------------+-------+
2 rows in set (0.00 sec)

-- 重新設置最大連接數
set global max_connections=1000;
複製代碼

在 /etc/my.cnf 裏面設置數據庫的最大連接數:

[mysqld]
max_connections = 1000

查看當前連接數

複製代碼
mysql> show status like  'Threads%';
+-------------------+-------+
| Variable_name     | Value |
+-------------------+-------+
| Threads_cached    | 32    |
| Threads_connected | 10    |
| Threads_created   | 50    |
| Threads_rejected  | 0     |
| Threads_running   | 1     |
+-------------------+-------+
5 rows in set (0.00 sec)
複製代碼
  • Threads_connected:表示當前連接數。跟 show processlist 結果相同。準確的來說,Threads_running 代表的是當前併發數。
  • Threads_running:表示激活的連接數。一般遠低於 connected 數值。
  • Threads_created:表示創建過的線程數。
  • 如果我們在 MySQL 服務器配置文件中設置了 thread_cache_size,那麼當客戶端斷開之後,服務器處理此客戶的線程將會緩存起來以響應下一個客戶而不是銷燬(前提是緩存數未達上限)。
  • 如果發現 Threads_created 值過大的話,表明 MySQL 服務器一直在創建線程,這也是比較耗資源,因此可以適當增加配置文件中 thread_cache_size 值。

查詢服務器 thread_cache_size 的值

複製代碼
mysql> show variables like 'thread_cache_size';
+-------------------+-------+
| Variable_name | Value |
+-------------------+-------+
| thread_cache_size | 100 |
+-------------------+-------+
1 row in set (0.00 sec)
複製代碼

  

4.2 執行計劃(explain)詳解

EXPLAIN 命令可以獲取 MySQL 如何執行 SELECT 語句的信息。因此平時在進行 SQL 開發時,都要養成用 explain 分析的習慣。

詳見《explain 命令詳解》。

  

4.3 索引設計策略

詳見《MySQL 索引》。  

  

4.4 SQL 優化

即使數據庫表的索引設置已經比較合理,但 SQL 語句書寫不當的話,也會造成索引失效,甚至造成全表掃描,從而拉低性能。 

開啓查詢緩存(MySQL 8.0 已廢棄該功能)

大多數的 MySQL 服務器都開啓了查詢緩存。這是提高性最有效的方法之一,而且這是被 MySQL 的數據庫引擎處理的。當有很多相同的查詢被執行了多次的時候,這些查詢結果會被放到一個緩存中,這樣,後續的相同的查詢就不用操作表而直接訪問緩存結果了。

這裏最主要的問題是,對於程序員來說,這個事情是很容易被忽略的。因爲我們的某些查詢語句會讓 MySQL 不使用緩存。請看下面的示例:

SELECT username FROM user WHERE signup_date >= CURDATE();  -- 不走緩存

SELECT username FROM user WHERE signup_date >= '2014-06-24';  -- 走緩存

上面兩條 SQL 語句的差別就是 CURDATE() ,MySQL 的查詢緩存對這個函數不起作用。所以,像 NOW() 和 RAND() 或是其它的諸如此類的 SQL 函數都不會開啓查詢緩存,因爲這些函數的返回是不確定的。

7f95ffb0ad2d9633f27af0446977f3f5.png

使用連接查詢代替子查詢

對於數據庫來說,在絕大部分情況下,連接會比子查詢更快,使用連接的方式,MySQL 優化器一般可以生成更佳的執行計劃,更高效地處理查詢。

而子查詢往往需要運行重複的查詢,子查詢生成的臨時表上也沒有索引, 因此效率會更低。

當只要一行數據時使用 LIMIT 1

針對非主鍵的其他查詢,加上 LIMIT 1 可以增加性能。這樣 MySQL 數據庫引擎會在找到一條數據後停止搜索,而不是繼續往後查下一條符合記錄的數據(否則即使已經查到一條結果,也會繼續查詢是否還存在等值結果,再返回結果)。

多表關聯查詢時,小表在前,大表在後

在 MySQL 中,執行 from 後的表關聯查詢是從左往右執行的,第一張表會涉及到全表掃描,所以將小表放在前面,先掃小表,掃描快效率較高,在掃描後面的大表,或許只掃描大表的前 100 行就符合返回條件並 return 了。

調整 where 子句中的連接順序

MySQL 採用從左往右的順序解析 where 子句,可以將過濾數據多的條件放在前面,最快速度縮小結果集。

不要使用 ORDER BY RAND()

想打亂返回的數據行?隨機挑一個數據?但你卻不瞭解這樣做有多麼可怕的性能問題。

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

優化 GROUP BY

使用 GROUP BY 但要避免排序結果的消耗。

GROUP BY … ORDER BY NULL;  -- 禁止排序

JOIN 查詢

如果你的應用程序有很多 JOIN 查詢,你應該確認兩個表中 JOIN 的字段是被建過索引的。這樣,MySQL 內部會啓動爲你優化 JOIN 語句的機制。

而且,這些被用來 JOIN 的字段,應是相同類型的。例如:如果你要把 DECIMAL 字段和一個 INT 字段 JOIN 在一起,MySQL 就無法使用它們的索引。對於 STRING 類型,還需要有相同的字符集纔行(兩個表的字符集有可能不一樣)。

SELECT company_name FROM users
LEFT JOIN companies ON users.state = companies.state
WHERE users.id = ...

例如以上兩個 state 字段應該是被建過索引的,而且應是相當類型、相同字符集的。

 

4.5 表結構優化 

永遠爲每張表創建主鍵

我們應該爲數據庫裏的每張表都設置一個 id 作爲主鍵,最好還是 INT 類型的(推薦使用 UNSIGNED 即無符號化),並設置上自動增加的 AUTO_INCREMENT 標誌。

  • 表數據的存儲在磁盤中是按照主鍵順序存放的,所以使用主鍵查詢數據速度最快。
  • INT 類型相比字符串類型,其長度更爲固定,查詢效率更高。
  • 還有一些操作需要用到主鍵,比如集羣、分區等。在這些情況下,主鍵的性能和設置變得非常重要。

所以建表時一定要帶有主鍵,後續優化效果最好。

固定長度的表會更快

如果表中的所有字段都是“固定長度”的,整個表會被認爲是 “static” 或 “fixed-length”。 例如,表中沒有如下類型的字段: VARCHAR、TEXT、BLOB。只要你包括了其中一個這些字段,那麼這個表就不是“固定長度靜態表”了,這樣,MySQL 引擎會用另一種方法來處理。

固定長度的表會提高性能,因爲 MySQL 搜尋得會更快一些,因爲這些固定的長度是很容易計算下一個數據的偏移量,所以讀取的自然也會很快。而如果字段不是定長的,那麼,每一次要找下一條的話,需要程序找到主鍵。

並且,固定長度的表也更容易被緩存和重建。不過,唯一的副作用是,固定長度的字段會浪費一些空間,因爲定長的字段無論你用不用,他都是要分配那麼多的空間。

通過拆分表,提高訪問效率

把數據庫中的表按列變成幾張表的方法,這樣可以降低表的複雜度和字段的數目,從而達到優化的目的。

越小的列會越快

對於大多數的數據庫引擎來說,硬盤操作可能是最重大的瓶頸。所以,把你的數據變得緊湊會對這種情況非常有幫助,因爲這減少了對硬盤的訪問。

如果一個表只會有幾列罷了(比如說字典表、配置表),那麼,我們就沒有理由使用 INT 來做主鍵,使用 MEDIUMINT、SMALLINT 或是更小的 TINYINT 會更經濟一些。如果你不需要記錄時間,使用 DATE 要比 DATETIME 好得多。

使用 ENUM 而不是 VARCHAR

ENUM 類型是非常快和緊湊的。實際上其保存的是 TINYINT,但其外表上顯示爲字符串。這樣一來,用這個字段來做一些選項列表變得相當的完美。

如果你有一個字段,比如“性別”,“國家”,“民族”,“狀態”或“部門”,你知道這些字段的取值是有限而且固定的,那麼,你應該使用 ENUM 而不是 VARCHAR。

 

4.6 事務和鎖優化

參見《MySQL 事務和鎖》

 

4.7 MySQL 服務端參數優化

Innodb_buffer_pool_size

影響性能的最主要參數,一般建議配置爲系統總內存的 70-80%,這個參數決定了服務可分配的最大內存。

複製代碼
-- 通過 Buffer Pool 的實時狀態信息來確定 InnoDB 的 Buffer Pool 的使用是否高效
mysql> show status like 'Innodb_buffer_pool_%';
+---------------------------------------+-------------+
| Variable_name                         | Value       |
+---------------------------------------+-------------+
| Innodb_buffer_pool_dump_status        | not started |
| Innodb_buffer_pool_load_status        | not started |
| Innodb_buffer_pool_pages_data         | 446         |
| Innodb_buffer_pool_bytes_data         | 7307264     |
| Innodb_buffer_pool_pages_dirty        | 0           |
| Innodb_buffer_pool_bytes_dirty        | 0           |
| Innodb_buffer_pool_pages_flushed      | 110         |
| Innodb_buffer_pool_pages_free         | 12864       |
| Innodb_buffer_pool_pages_misc         | 2           |
| Innodb_buffer_pool_pages_total        | 13312       |
| Innodb_buffer_pool_read_ahead_rnd     | 0           |
| Innodb_buffer_pool_read_ahead         | 0           |
| Innodb_buffer_pool_read_ahead_evicted | 0           |
| Innodb_buffer_pool_read_requests      | 10293       |
| Innodb_buffer_pool_reads              | 432         |
| Innodb_buffer_pool_wait_free          | 0           |
| Innodb_buffer_pool_write_requests     | 380         |
+---------------------------------------+-------------+
17 rows in set (0.00 sec)
複製代碼

Innodb_log_buffer_size

顧名思義,這個參數就是用來設置 Innodb 的 Log Buffer 大小的,系統默認值爲 1MB 。 Log Buffer 的主要作用就是緩衝 Log 數據,提高寫 Log 的 I/O 性能。

一般來說,如果你的系統不是寫負載非常高且以大事務居多的話, 8MB 以內的大小就完全足夠了。

複製代碼
-- 查看innodb_log_buffer_size 設置是否合理
mysql> show status like 'innodb_log%';
+---------------------------+-------+
| Variable_name             | Value |
+---------------------------+-------+
| Innodb_log_waits          | 0     |
| Innodb_log_write_requests | 61    |
| Innodb_log_writes         | 25    |
+---------------------------+-------+
3 rows in set (0.00 sec)
複製代碼

刷盤策略

  • Sync_binlog:這是控制日誌刷盤策略的,基於安全一般設置爲 1。
  • Innodb_flush_log_at_trx_commit:這個是控制事務日誌刷盤策略,基於安全一般設置爲 1。

上面兩個參數設置爲 1 時最安全,但對於磁盤 I/O 消耗更大;0 時爲最大性能,但故障切換時容易丟數據。

InnoDB 性能監控

-- 持續獲取狀態信息的方法
create table innodb_monitor(a int) engine=innodb; 

創建一個 innodb_monitor 空表後,InnoDB 會每隔 15 秒輸出一次信息並記錄到 Error Log 中。通過刪除該表可停止監控。

除此之外,我們還可以通過相同的方式打開和關閉 innodb_tablespace_monitor、innodb_lock_monitor、innodb_table_monitor 這三種監控功能。

 

4.8 硬件優化

不同的應用或者進程,對於硬件資源的要求是不同的。比如有計算密集型、I/O 密集型等。

  • 關係型數據庫的要求:多 CPU、高內存、高磁盤 I/O 的一種服務。
  • Redis 的要求:單 CPU、高內存、對磁盤要求低。

對於 MySQL,硬件如何選擇和優化呢:

  • CPU:多核高頻。
  • 內存:高內存。
  • 磁盤:選擇 SSD;且一般要求做 RAID(磁盤陣列)。

RAID(磁盤陣列)

根據數據分佈和冗餘方式,RAID 分爲許多級別。不同存儲廠商提供的 RAID 卡或設備,其支持的 RAID 級別也不盡相同。以下介紹最常見也是最基本的幾種,其他 RAID 級別基本上都是在這幾種基礎上的改進。 

RAID

級別

特性 優點 缺點
RAID 0 也叫條帶化(Stripe),按一定的條帶大小(Chunk Size)將數據依次分佈到各個磁盤,沒有數據冗餘。 數據併發讀寫速度快,無額外的磁盤空間開銷,投資省。 數據無冗餘保護,可靠性差。
RAID 1 也叫磁盤鏡像(Mirror),兩個磁盤一組,所有數據都同時寫入兩個磁盤,但讀時從任一磁盤讀都可以。

數據有完全冗餘保護,只要不出現兩塊鏡像磁盤同時損壞,就不會影響使用;

可以提高併發讀性能。

容量一定的話,需要 2 倍的磁盤,投資比較大。
RAID 10 是 RAID 1 和 RAID 0 的結合,也叫 RAND 1+0。先對磁盤做鏡像,再條帶話,使其兼具 RAID 1 的可靠性和 RAID 0 的優良併發讀寫性能。 可靠性高,併發讀寫性能優良。 容量一定的話,需要 2 倍的磁盤,投資比較大。
RAID 4  像 RAID 0 一樣對磁盤組條帶化,不同的是:需要額外增加一個磁盤,用來寫各 Stripe 的校驗糾錯數據。

RAID 中的一個磁盤損壞的話,其數據可以通過校驗糾錯數據計算出來,具有一定容錯保護能力;

讀數據速度快。

每個 Stripe 上數據的修改都要寫校驗糾錯塊,寫性能受影響;

所有糾錯數據都在同一磁盤上,風險大,也會形成一個性能瓶頸;

在出現壞盤時,讀性能會下降。

RAID 5 是對 RAID4 的改進:將每一個條帶(Stripe)的校驗糾錯數據塊也分佈寫到各個磁盤,而不是寫到一個特定的磁盤。 基本同 RAID 4,只是其寫性能和數據保護能力要更強一 點。

寫性能不及 RAID 0、 RAID 1 和 RAID 10,容錯能力也不及 RAID 1;

在出現壞盤時,讀性能會下降。

如何選擇 RAID 級別

瞭解各種 RAID 級別的特性後,我們就可以根據數據讀寫的特點、可靠性要求,以及投資預算等來選擇合適的 RAID 級別,比如:

  • 數據讀寫都很頻繁,可靠性要求也很高,最好選擇 RAID 10。
  • 數據讀很頻繁,寫相對較少,對可靠性有一定要求,可以選擇 RAID 5。
  • 數據讀寫都很頻繁,但可靠性要求不高,可以選擇 RAID 0。

虛擬文件卷或軟 RAID

最初,RAID 都是由硬件實現的,要使用 RAID,至少需要有一個 RAID 卡。但現在,一些操作系統中提供的軟件包,也模擬實現了一些 RAID 的特性,雖然性能上不如硬 RAID,但相比單個磁盤,性能和可靠性都有所改善。比如:

  • Linux下的邏輯卷(Logical Volume)系統 lvm2,支持條帶化(Stripe)。
  • Linux 下的 MD(Multiple Device)驅動,支持 RAID 0、RAID 1、RAID 4、RAID 5、RAID 6 等。

在不具備硬件條件的情況下,可以考慮使用上述虛擬文件卷或軟 RAID 技術,具體配置方法可參見 Linux 幫助文檔。

 

4.9 架構優化

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