MySQL 服務器性能剖析

這是《高性能 MySQL(第三版)》第三章的讀書筆記。php

關於服務,常見的問題有:html

  • 如何確認服務器是否發揮了最大性能
  • 找出執行慢的語句,爲什麼執行慢
  • 爲什麼在用戶端發生間歇性的停頓、卡死

經過性能剖析(profiling)分析服務器的性能並找出花費時間最多的地方,有助於解決上述問題。mysql

1. 性能優化簡介

性能一般能夠認爲就是響應時間(Latency,延遲),而性能優化就是減小響應時間。nginx

要想優化性能,首先須要知道性能的瓶頸在哪裏。這就須要用到測試了。c++

2. 性能測試

2.1 對應用程序進行性能測試

每種語言都有專門的測試程序,對於 PHP,可使用的有:git

  • xhprof:輕量小巧,可在生成環境部署。參考 這裏
  • xdebug:檢測範圍大,開銷大,適合測試環境。

2.2 對數據庫進行性能測試

2.2.1 分析服務器負載

經過分析服務器負載,定位須要優化的單條查詢。github

捕獲 MySQL 的查詢到日誌文件

MySQL 中,慢查詢日誌是開銷最低、精度最高的測量查詢時間的工具。運行時間超過 long_query_time 值語句會被記錄到慢查詢日誌中。long_query_time 單位是秒,默認值爲 10,能夠設置爲 0 來捕獲全部的查詢。慢查詢日誌須要手動開啓,開啓後會略微影響性能。慢查詢日誌能夠寫入文件或數據庫,但寫入到數據庫時會影響性能且時間粒度退化爲秒級。web

MySQL 中還有另一種查詢日誌,叫通用日誌,但可用信息太少且資源消耗大,基本不用。算法

慢查詢日誌相關參數以下,使用能夠參考 這裏sql

  • slow_query_log:是否開啓慢查詢日誌,1 開啓,0 關閉。
  • log-slow-queries:舊版(5.6如下版本)MySQL數據庫慢查詢日誌存儲路徑。默認給一個缺省的文件 host_name-slow.log
  • slow-query-log-file:新版(5.6及以上版本)MySQL數據庫慢查詢日誌存儲路徑。默認給一個缺省的文件 host_name-slow.log
  • long_query_time :慢查詢閾值,當查詢時間多於設定的閾值時,記錄日誌。
  • log_queries_not_using_indexes:未使用索引的查詢也被記錄到慢查詢日誌中(可選項)。
  • log_output:日誌存儲方式。log_output=’FILE’表示將日誌存入文件,默認值是’FILE’。log_output=’TABLE’表示將日誌存入數據庫,這樣日誌信息就會被寫入到 mysql.slow_log 表中。MySQL 數據庫支持同時兩種日誌存儲方式,配置的時候以逗號隔開便可,如:log_output=’FILE,TABLE’。日誌記錄到系統的專用日誌表中比記錄到文件耗費更多的系統資源,所以對於須要啓用慢查詢日誌,又須要可以得到更高的系統性能,建議優先記錄到文件。

分析查詢日誌

分析慢查詢日誌可使用 MySQL 自帶的 mysqldumpslow 工具。要用慢查詢日誌生成分析報告可使用 pt-query-digest 工具。

2.2.2 分析單條查詢

定位到須要優化的單條查詢後,開始優化。

SHOW PROFILE(以被 performance_schema 替代)

都廢棄了,別研究了。

–使用前先經過 have_profiling 參數查看當前 MySQL是否支持 profile:–

mysql> select @@have_profiling; +------------------+
| @@have_profiling | +------------------+
| YES | +------------------+
1 row in set, 1 warning (0.01 sec)

mysql> show warnings; +---------+------+---------------------------------------------------------------------------+
| Level | Code | Message | +---------+------+---------------------------------------------------------------------------+
| Warning | 1287 | '@@have_profiling' is deprecated and will be removed in a future release. | +---------+------+---------------------------------------------------------------------------+
1 row in set (0.00 sec)

–默認禁用,能夠經過服務器變量在會話級別啓用:–

mysql> SET profiling = 1;

Performace Schema

參考這裏

Performace Schema 是目前 MySQL(5.7 及以上版本)主推的性能測試工具,默認開啓。首次安裝 MySQL 後,你會發現其默認安裝了幾個數據庫,其中的 performace_schema 就是用於配置 Performace Schema 的。

SHOW STATUS

SHOW STATUS 命令返回一些服務器級別的全局計數器或某個會話級別的計數器。能夠顯示活動頻繁程度,可是沒法給出消耗的時間。

mysql> show global status; +-----------------------------------------------+--------------------------------------------------+
| Variable_name | Value | +-----------------------------------------------+--------------------------------------------------+
| Aborted_clients | 275 | | Aborted_connects                              | 80872                                            |
| Binlog_cache_disk_use | 0 | ... | Threads_running                               | 1                                                |
| Uptime                                        | 6661773                                          |
| Uptime_since_flush_status | 6661773 | | validate_password_dictionary_file_last_parsed | 2018-01-31 15:35:09                              |
| validate_password_dictionary_file_words_count | 0 | +-----------------------------------------------+--------------------------------------------------+
355 rows in set (0.03 sec)

2.3 診斷間歇問題

應用偶爾停頓、數據庫間歇性的慢查詢的可能緣由有:

  • 應用經過接口從運行很慢的外部服務獲取數據
  • memcached 中的重要緩存條目過時,致使大量請求落到 MySQL 以從新生成緩存條目
  • DNS 查詢偶爾超時
  • 互斥鎖爭用或內部刪除查詢緩存的算法效率過低,MySQL 的查詢緩存致使服務短暫停頓
  • 併發度超過閾值時,InnoDB 的擴展性限制致使查詢計劃的優化須要很長時間

解決間歇性問題有常見的套路和工具,定位好問題才能進行以後的操做。

2.3.1 單條查詢問題仍是服務器問題

SHOW GLOBAL STATUS

SHOW STATUS 或 SHOW SESSION STATUS 顯示當前會話級別的信息,SHOW GLOBAL STATUS 顯示服務器級別的信息。完整差別能夠參考 SHOW GLOBAL STATUS vs SHOW STATUS

以較高的頻率執行 SHOW GLOBAL STATUS 命令捕獲數據,問題出現時能夠經過某些計數器(Threads_running、Threads_connected、Questions、Queries)的「尖刺」和「凹陷」來發現問題。SHOW GLOBAL STATUS 命令的用法簡單且執行時不須要特殊權限(登陸數據庫的用戶均可以使用)、對服務器影響小。

下面示例經過 -i1 選項每秒捕捉一次數據,輸出給 awk 計算並輸出每秒的查詢數、Threads_connected、Threads_running:

[root@VM_120_242_centos ~]# mysqladmin -uroot -p ext -i1 | awk '/Queries/{q=$4-qp;qp=$4} /Threads_connected/{tc=$4} /Threads_running/{printf "%d %d %d\n", q, tc, $4}' Enter password: 1554866 1 1 1 1 1 1 1 1

下面示例經過 -i1 選項每秒捕捉一次數據,經過 -r 選項展現差值,輸出給 awk 計算並輸出每秒的查詢數:

# mysqladmin -uroot -p ext -i1 -r | awk '/Queries/{q=$4} /Threads_connected/{tc=$4} /Threads_running/{printf "%d %d %d\n", q, tc, $4}'
Enter password: 
1554856 1 1
1 0 0
1 0 0
1 0 0

SHOW PROCESSLIST

不停的捕獲 SHOW PROCESSLIST 的輸出,能夠觀察是否有大量線程處於不正常狀態或有不正常特徵。例如查詢不多會長時間處於「statictics」狀態。

示例,經過命令尾部使用 \G 替換分號能夠垂直輸出結果,經過 sort|uniq|sort 命令能夠計算某個列值出現的次數:

[root@VM_120_242_centos ~]# mysql -uroot -p -e 'SHOW PROCESSLIST\G' | grep State: | sort | uniq -c | sort -rn
Enter password: 
      2   State: 
      1   State: starting

除了經過命令行外,也能夠直接查詢 INFORMATION_SCHEMA 數據庫中的 PROCESSLIST 表。

使用查詢日誌

須要開啓慢查詢日誌,並在全局級別設置 long_query_time 爲 0,而後重置全部鏈接以使新的全局設置生效。

好的工具能夠幫助診斷問題,不然就要去幾百 GB 的日誌文件中查找問題。下面示例的一行代碼能夠根據 MySQL 每秒將當前時間寫入日誌中的模式統計每秒的查詢數量:

awk '/^# Time:/{print $3, $4, c; c=0}/^# User/{c++}' slow-query.log

理解發現的問題

建議剛開始診斷問題時,先使用 SHOW STATUS 和 SHOW PROCESSLIST。這兩種方法開銷低,且能夠經過 shell 或反覆查詢來收集數據。分析慢查詢日誌則比較困難。

2.3.2 捕獲診斷數據

診斷間歇性問題,須要收集儘量多的數據。須要兩個工具:

  • 觸發器,即區分問題的方法
  • 收集診斷數據的工具

診斷觸發器

觸發器須要避免「誤報」和「漏檢」。

一般將觸發器閾值調整爲正常狀況的一倍左右。好比 Threads_running 在正常狀況下是 10,則觸發器的閾值能夠設置爲 20。對於 Threads_connected 若是正常值爲 150,則閾值能夠是 300。

另外,還須要設置持續時間。好比 Threads_running 連續 3 秒鐘超過 20,則認爲是異常狀況。可使用「pt-stalk」工具。

須要收集的數據類型

肯定診斷觸發器後,能夠開啓進程收集數據。數據應該儘量多,包括系統狀態、CPU 利用率、磁盤使用率和可用空間、內存利用率、ps 命令的輸出採樣,以及從 MySQL 得到的信息,包括 SHOW STATUS、SHOW INNODB STATUS、SHOW PROCESSLIST。

Linux 上可用的服務器內部診斷工具備 oprofile、strace(生產環境中使用有風險)。要剖析查詢可用 tcpdump。

能夠經過 GDB 堆棧跟蹤分析等待緣由。跟蹤時先啓動 GDB,而後附加(attach)到 mysqld 進程,將全部線程的堆棧都存儲起來。而後利用腳本彙總相似的堆棧信息,再利用 sort|uniq|sort 排序。

解釋結果數據

2.3.3 案例

2.4 其餘分析工具

2.4.1 USER_STATISTICS 表

INFORMATION_SCHEMA 庫中的表,存儲了各個數據庫的信息及全部活動的統計信息,能夠得知哪一個數據庫的哪一個表、哪一個索引使用的最頻繁。
表索引的信息。SHOW INDEX FROM schemaname.tablename; 命令從這個表獲取結果。

mysql> SHOW INDEX FROM szhuizhong.users; +-------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| Table | Non_unique | Key_name | Seq_in_index | Column_name | Collation | Cardinality | Sub_part | Packed | Null | Index_type | Comment | Index_comment | +-------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+
| users |          0 | PRIMARY       |            1 | UserID      | A         |        1460 |     NULL | NULL   |      | BTREE      |         |               |
| users |          0 | Account_index | 1 | Account | A | 1460 | NULL | NULL | | BTREE | | | | users | 1 | CorpID | 1 | FromID | A | 2 | NULL | NULL | YES | BTREE | | | +-------+------------+---------------+--------------+-------------+-----------+-------------+----------+--------+------+------------+---------+---------------+ 3 rows in set (0.00 sec)

5. 總結

要解決問題,首先要搞清楚問題。全部的查詢記錄都要記錄到日誌中,而後用 pt-query-digest 工具生成系統級別的剖析報告。

相關文章
相關標籤/搜索