分享到

簡介

對於將資料庫整合到其技術棧中的網站和應用程式而言,使用者體驗的很大一部分會受到資料庫效能的影響。慢查詢可能會延遲資料檢索、頁面渲染以及任何與資料層互動的操作。由於可能造成嚴重影響,因此瞭解如何識別和解決這些問題至關重要。

在本文中,我們將討論識別 MySQL 資料庫中效能不佳查詢的各種方法。這將為最佳化這些查詢並提高其效能奠定基礎。

檢查活躍查詢和程序

要獲取 MySQL 當前執行狀態的概覽,最直接的檢查位置之一是其程序列表

顯示完整程序列表

要顯示 MySQL 處理執行緒正在執行的所有當前操作,請輸入

SHOW FULL PROCESSLIST;
+----+-----------------+-----------+------+---------+---------+------------------------+-----------------------+
| Id | User | Host | db | Command | Time | State | Info |
+----+-----------------+-----------+------+---------+---------+------------------------+-----------------------+
| 5 | event_scheduler | localhost | NULL | Daemon | 1834441 | Waiting on empty queue | NULL |
| 50 | root | localhost | NULL | Query | 0 | init | show full processlist |
+----+-----------------+-----------+------+---------+---------+------------------------+-----------------------+
2 rows in set (0.00 sec)

以上輸出顯示了一個空閒伺服器,其中只有我們自己的查詢以及一個長時間執行的事件監聽器。一個活躍的伺服器會顯示更多的程序,其中一些可能是長時間執行的。如果沒有 FULL 修飾符,此命令將只顯示前 100 個程序,這可能會或可能不會根據您的伺服器活動截斷結果。

需要檢視的一些重要部分是 TimeState 列。Time 列計算執行緒處於所述 State 的秒數。如果您發現某些程序的 Time 值與您對給定操作的預期不符,那麼可能是時候進一步調查了。

檢查儲存引擎狀態

另一個可以檢查的地方是實際儲存引擎的狀態。

您可以透過輸入以下命令找到與給定表關聯的儲存引擎

SHOW CREATE TABLE <database>.<table>\G

例如,要顯示 mysql.time_zone 表使用的儲存引擎,請輸入

SHOW CREATE TABLE mysql.time_zone`\G
*************************** 1. row ***************************
Table: time_zone
Create Table: CREATE TABLE `time_zone` (
`Time_zone_id` int unsigned NOT NULL AUTO_INCREMENT,
`Use_leap_seconds` enum('Y','N') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'N',
PRIMARY KEY (`Time_zone_id`)
) /*!50100 TABLESPACE `mysql` */ ENGINE=InnoDB DEFAULT CHARSET=utf8mb3 STATS_PERSISTENT=0 ROW_FORMAT=DYNAMIC COMMENT='Time zones'
1 row in set (0.01 sec)

ENGINE=InnoDB 表示該表正在使用 InnoDB 儲存引擎。這是大多數配置中的預設儲存引擎,因此您可能需要檢查其狀態。

您可以透過輸入以下命令顯示 InnoDB 引擎的狀態:

SHOW ENGINE INNODB STATUS\G
*************************** 1. row ***************************
Type: InnoDB
Name:
Status:
=====================================
2022-02-26 11:55:52 139992513984256 INNODB MONITOR OUTPUT
=====================================
Per second averages calculated from the last 16 seconds
-----------------
BACKGROUND THREAD
-----------------
srv_master_thread loops: 1 srv_active, 0 srv_shutdown, 1835091 srv_idle
srv_master_thread log flush and writes: 0
----------
SEMAPHORES
----------
OS WAIT ARRAY INFO: reservation count 555
OS WAIT ARRAY INFO: signal count 457
RW-shared spins 0, rounds 0, OS waits 0
RW-excl spins 0, rounds 0, OS waits 0
RW-sx spins 0, rounds 0, OS waits 0
Spin rounds per wait: 0.00 RW-shared, 0.00 RW-excl, 0.00 RW-sx
------------
TRANSACTIONS
------------
Trx id counter 8778
Purge done for trx's n:o < 8713 undo n:o < 0 state: running but idle
History list length 0
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 421467955133656, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 421467955132848, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
---TRANSACTION 421467955132040, not started
0 lock struct(s), heap size 1128, 0 row lock(s)
--------
FILE I/O
--------
I/O thread 0 state: waiting for completed aio requests (insert buffer thread)
I/O thread 1 state: waiting for completed aio requests (log thread)
I/O thread 2 state: waiting for completed aio requests (read thread)
I/O thread 3 state: waiting for completed aio requests (read thread)
I/O thread 4 state: waiting for completed aio requests (read thread)
I/O thread 5 state: waiting for completed aio requests (read thread)
I/O thread 6 state: waiting for completed aio requests (write thread)
I/O thread 7 state: waiting for completed aio requests (write thread)
I/O thread 8 state: waiting for completed aio requests (write thread)
I/O thread 9 state: waiting for completed aio requests (write thread)
Pending normal aio reads: [0, 0, 0, 0] , aio writes: [0, 0, 0, 0] ,
ibuf aio reads:, log i/o's:, sync i/o's:
Pending flushes (fsync) log: 0; buffer pool: 0
1779 OS file reads, 3384 OS file writes, 1870 OS fsyncs
0.00 reads/s, 0 avg bytes/read, 0.00 writes/s, 0.00 fsyncs/s
-------------------------------------
INSERT BUFFER AND ADAPTIVE HASH INDEX
-------------------------------------
Ibuf: size 1, free list len 0, seg size 2, 0 merges
merged operations:
insert 0, delete mark 0, delete 0
discarded operations:
insert 0, delete mark 0, delete 0
Hash table size 34679, node heap has 2 buffer(s)
Hash table size 34679, node heap has 9 buffer(s)
Hash table size 34679, node heap has 9 buffer(s)
Hash table size 34679, node heap has 15 buffer(s)
Hash table size 34679, node heap has 7 buffer(s)
Hash table size 34679, node heap has 3 buffer(s)
Hash table size 34679, node heap has 4 buffer(s)
Hash table size 34679, node heap has 6 buffer(s)
0.00 hash searches/s, 0.00 non-hash searches/s
---
LOG
---
Log sequence number 38540718
Log buffer assigned up to 38540718
Log buffer completed up to 38540718
Log written up to 38540718
Log flushed up to 38540718
Added dirty pages up to 38540718
Pages flushed up to 38540718
Last checkpoint at 38540718
2197 log i/o's done, 0.00 log i/o's/second
----------------------
BUFFER POOL AND MEMORY
----------------------
Total large memory allocated 0
Dictionary memory allocated 473011
Buffer pool size 8192
Free buffers 6241
Database pages 1896
Old database pages 719
Modified db pages 0
Pending reads 0
Pending writes: LRU 0, flush list 0, single page 0
Pages made young 8, not young 0
0.00 youngs/s, 0.00 non-youngs/s
Pages read 1754, created 142, written 926
0.00 reads/s, 0.00 creates/s, 0.00 writes/s
No buffer pool page gets since the last printout
Pages read ahead 0.00/s, evicted without access 0.00/s, Random read ahead 0.00/s
LRU len: 1896, unzip_LRU len: 0
I/O sum[0]:cur[0], unzip sum[0]:cur[0]
--------------
ROW OPERATIONS
--------------
0 queries inside InnoDB, 0 queries in queue
0 read views open inside InnoDB
Process ID=59529, Main thread ID=139992560166656 , state=sleeping
Number of rows inserted 0, updated 0, deleted 0, read 0
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
Number of system rows inserted 0, updated 317, deleted 0, read 6128
0.00 inserts/s, 0.00 updates/s, 0.00 deletes/s, 0.00 reads/s
----------------------------
END OF INNODB MONITOR OUTPUT
============================
1 row in set (0.00 sec)

輸出將包含大量資訊,包括引擎正在使用的資源、正在執行的程序等。您可以使用它來了解執行是否存在瓶頸,或者競爭的程序數量是否導致效能問題。

啟用慢查詢日誌

獲取長時間執行或慢速執行查詢更多資訊的一種方法是使用慢查詢日誌。慢查詢日誌會指示 MySQL 記錄任何超過特定執行閾值的查詢。這對於精確定位執行不佳的特定查詢非常有用,而無需即時在程序列表中捕獲它。

檢查 MySQL 是否正在記錄慢查詢

您首先應該做的是驗證慢查詢日誌的當前狀態。如果慢查詢日誌已啟用,您無需執行任何操作。

您可以透過輸入以下命令檢查慢查詢日誌是否已啟用:

SHOW GLOBAL VARIABLES LIKE 'slow_query_log%';
+---------------------+------------------------------------+
| Variable_name | Value |
+---------------------+------------------------------------+
| slow_query_log | OFF |
| slow_query_log_file | /var/lib/mysql/mysqlutils-slow.log |
+---------------------+------------------------------------+
2 rows in set (0.01 sec)

以上輸出表明慢查詢目前未被記錄,因為該功能已關閉。

如果慢查詢日誌已開啟,您的輸出將看起來像這樣:

+---------------------+-------------------------------+
| Variable_name | Value |
+---------------------+-------------------------------+
| slow_query_log | ON |
| slow_query_log_file | /var/log/mysql/mysql-slow.log |
+---------------------+-------------------------------+
2 rows in set (0.02 sec)

現在您知道了當前狀態,可以根據需要進行更改。

配置 MySQL 以記錄慢查詢

在我們繼續之前,重要的是要注意,雖然慢查詢日誌非常有用,但它可能會對效能產生額外的開銷。MySQL 必須執行額外的操作來計時每個查詢並將結果記錄到日誌中。這可能會影響效能並意外地佔用硬碟空間。

不建議始終記錄慢查詢。相反,在您積極調查問題時啟用此功能,並在完成後停用它。

考慮到這一點,您可以透過修改 MySQL 伺服器的配置檔案來配置慢查詢日誌。您也可以互動式地修改這些值,但在配置中設定良好的預設值將使以後互動式調整變得更容易。

開啟 MySQL 的配置檔案。在大多數基於 Debian Linux 的系統上,配置檔案位於 /etc/mysql/mysql.conf.d/mysqld.conf

sudo vim /etc/mysql/mysql.conf.d/mysqld.conf

我們將需要修改或可能新增以下設定:

變數設定描述
slow_query_logON切換是否啟用慢查詢。
slow_query_log_file/var/log/mysql/mysql-slow.log記錄慢查詢的日誌檔案。
long_query_time(時間,單位秒)查詢被視為“慢查詢”前必須經過的閾值(單位:秒)。
min_examined_row_limit(行數)查詢在成為慢查詢候選之前必須考慮的行數。
log_slow_admin_statementsON切換管理命令是否也受日誌記錄影響。
log_queries_not_using_indexesON切換如果查詢未查閱索引是否會被記錄。
log_slow_extraON對於 MySQL 8.0.14 或更高版本,這會切換是否記錄有關查詢的附加資訊。
log_slow_replica_statementsON對於 MySQL 8.0.26 或更高版本,這會切換是否記錄在副本上執行的慢語句。這僅適用於 binlog_format 設定為 STATEMENTMIXED 的語句。
log_slow_slave_statementsON對於 MySQL 8.0.25 或更早版本,這會切換是否記錄在副本上執行的慢語句。這僅適用於 binlog_format 設定為 STATEMENTMIXED 的語句。

因此,例如,如果我們想開啟所有可選日誌記錄,並記錄任何檢查至少 100 行且執行時間超過 2 秒的語句,我們可以使用這些設定:

; enable MySQL to log slow queries
slow_query_log = ON
; query log location
slow_query_log_file = /var/log/mysql/mysql-slow.log
; amount of seconds that a query must surpass to be logged
long_query_time = 2
; minimum number of rows affected for a query to be logged
min_examined_row_limit = 100
; include administrative commands in the logs
log_slow_admin_statements = ON
; also log queries that are not using indexes
log_queries_not_using_indexes = ON
; for MySQL servers version 8.0.14 or later, we can
; get extra information by setting this
log_slow_extra = ON
; Also log slow statements that have executed on
; the replica
; This only will log statements if `binlog_format` is
; set to `STATEMENT` or `MIXED` and the statement is
; logged in statement format
; RE: https://dev.mysql.com.tw/doc/refman/8.0/en/replication-options-replica.html#sysvar_log_slow_replica_statements
; This option name depends on your MySQL version:
; versions >= 8.0.26: log_slow_replica_statements
; versions < 8.0.26: log_slow_slave_statements
; log_slow_slave_statements = ON
log_slow_replica_statements = ON

儲存並關閉檔案後,您可以透過輸入以下命令來驗證您的配置更改:

sudo mysqld --validate-config

如果沒有返回錯誤,則您的 MySQL 伺服器配置檔案在語法上是有效的。您可以透過輸入以下命令來重新啟動 MySQL 伺服器程序:

sudo systemctl restart mysql

您現在可以透過重新執行原始發現查詢來驗證慢查詢是否已啟用。

SHOW GLOBAL VARIABLES LIKE 'slow_query_log%';

一旦您按照自己的意願配置了慢查詢,就可以在 MySQL 內部根據需要啟用和停用它。調整值的語法如下所示:

SET GLOBAL slow_query_log = 'OFF';

使用 mysqldumpslow 分析慢查詢日誌

一旦您獲得了慢查詢日誌生成的檔案,您可以通過幾種不同的方式對其進行分析,以準確找出問題所在。

分析日誌最簡單的方法是使用 mysqldumpslow 實用程式,因為它包含在 MySQL 伺服器安裝中。要使用它,您可以將其指向您生成的慢查詢日誌:

sudo mysqldumpslow /var/log/mysql/mysql-slow.log
Reading mysql slow query log from /var/log/mysql/mysql-slow.log
Count: 4 Time=4.25s (17s) Lock=0.00s (0s) Rows=1.0 (4), root[root]@localhost
select sleep(N)

以上輸出顯示,根據我們的標準,有四個查詢被認為是“慢查詢”。它們都是 SELECT SLEEP(); 查詢的不同變體,命令中帶有不同的數字(由 N 佔位符表示)(如果您想測試,請確保 min_examined_row_limit 未設定)。執行這些語句的實際時間約為 17 秒。

mysqldumpslow 命令包含一些選項,用於控制輸出的排序和顯示。例如,您可以使用 -t 選項將結果限制為前“N”個。例如,以下顯示了前五個結果:

sudo mysqldumpslow -t 5 /var/log/mysql/mysql-slow.log

您可以使用 -s 選項更改排序順序。您可以按查詢時間 (t)、鎖定時間 (l)、傳送的行數 (r) 或這些指標的平均值(分別為 atalar)進行排序。預設情況下,mysqldumpslow 按平均查詢時間 (at) 排序。

要按鎖定時間量顯示前三個查詢,您可以輸入:

sudo mysqldumpslow -t 3 -s l /var/log/mysql/mysql-slow.log

使用 pt-query-digest 分析慢查詢日誌

另一個流行的慢查詢日誌分析工具是由 Percona 開發的 pt-query-digest 工具。pt-query-digest 工具是 Percona Toolkit 的一部分,後者是一套開源命令列工具,旨在幫助資料庫管理員更輕鬆地管理資料庫。

第一步是將 Percona Toolkit 下載到您的伺服器。您可以透過在 Percona Toolkit 下載頁面選擇您想要的工具包版本和您將使用的平臺來找到相應的檔案。

下載並安裝適用於您平臺的工具包版本後,您應該可以訪問 pt-query-digest 工具。

mysqldumpslow 相比,針對您的慢查詢日誌執行 pt-query-digest 會生成更多輸出:

sudo pt-query-digest /var/log/mysql/mysql-slow.log
# A software update is available:
# 680ms user time, 100ms system time, 44.71M rss, 59.35M vsz
# Current date: Sat Feb 26 13:06:41 2022
# Hostname: mysqlutils
# Files: /var/log/mysql/mysql-slow.log
# Overall: 4 total, 1 unique, 0.07 QPS, 0.30x concurrency ________________
# Time range: 2022-02-26T12:44:35 to 2022-02-26T12:45:32
# Attribute total min max avg 95% stddev median
# ============ ======= ======= ======= ======= ======= ======= =======
# Exec time 17s 2s 8s 4s 8s 2s 6s
# Lock time 0 0 0 0 0 0 0
# Rows sent 4 1 1 1 1 0 1
# Rows examine 4 1 1 1 1 0 1
# Bytes sent 224 56 56 56 56 0 56
# Query size 60 15 15 15 15 0 15
# Bytes receiv 0 0 0 0 0 0 0
# Created tmp 0 0 0 0 0 0 0
# Created tmp 0 0 0 0 0 0 0
# Errno 0 0 0 0 0 0 0
# Read first 0 0 0 0 0 0 0
# Read key 0 0 0 0 0 0 0
# Read last 0 0 0 0 0 0 0
# Read next 0 0 0 0 0 0 0
# Read prev 0 0 0 0 0 0 0
# Read rnd 0 0 0 0 0 0 0
# Read rnd nex 0 0 0 0 0 0 0
# Sort merge p 0 0 0 0 0 0 0
# Sort range c 0 0 0 0 0 0 0
# Sort rows 0 0 0 0 0 0 0
# Sort scan co 0 0 0 0 0 0 0
# Profile
# Rank Query ID Response time Calls R/Call V/M
# ==== =================================== ============== ===== ====== ===
# 1 0x59A74D08D407B5EDF9A57DD5A41825CA 17.0039 100.0% 4 4.2510 1.12 SELECT
# Query 1: 0.07 QPS, 0.30x concurrency, ID 0x59A74D08D407B5EDF9A57DD5A41825CA at byte 1266
# This item is included in the report because it matches --limit.
# Scores: V/M = 1.12
# Time range: 2022-02-26T12:44:35 to 2022-02-26T12:45:32
# Attribute pct total min max avg 95% stddev median
# ============ === ======= ======= ======= ======= ======= ======= =======
# Count 100 4
# Exec time 100 17s 2s 8s 4s 8s 2s 6s
# Lock time 0 0 0 0 0 0 0 0
# Rows sent 100 4 1 1 1 1 0 1
# Rows examine 100 4 1 1 1 1 0 1
# Bytes sent 100 224 56 56 56 56 0 56
# Query size 100 60 15 15 15 15 0 15
# Bytes receiv 0 0 0 0 0 0 0 0
# Created tmp 0 0 0 0 0 0 0 0
# Created tmp 0 0 0 0 0 0 0 0
# Errno 0 0 0 0 0 0 0 0
# Read first 0 0 0 0 0 0 0 0
# Read key 0 0 0 0 0 0 0 0
# Read last 0 0 0 0 0 0 0 0
# Read next 0 0 0 0 0 0 0 0
# Read prev 0 0 0 0 0 0 0 0
# Read rnd 0 0 0 0 0 0 0 0
# Read rnd nex 0 0 0 0 0 0 0 0
# Sort merge p 0 0 0 0 0 0 0 0
# Sort range c 0 0 0 0 0 0 0 0
# Sort rows 0 0 0 0 0 0 0 0
# Sort scan co 0 0 0 0 0 0 0 0
# String:
# End 2022-02-26... (1/25%), 2022-02-26... (1/25%)... 2 more
# Hosts localhost
# Start 2022-02-26... (1/25%), 2022-02-26... (1/25%)... 2 more
# Users root
# Query_time distribution
# 1us
# 10us
# 100us
# 1ms
# 10ms
# 100ms
# 1s ################################################################
# 10s+
# EXPLAIN /*!50100 PARTITIONS*/
select sleep(8)\G

輸出顯示了執行時間、查詢大小、鎖定時間、檢查和傳送的行數等等。pt-query-digest 命令有許多不同的選項,用於調整輸出並僅顯示您感興趣的專案。請查閱手冊頁以瞭解其功能。

結論

能夠發現查詢執行中的瓶頸對於維護資料庫和應用程式的效能至關重要。當出現效能下降時,制定策略來定位這些問題區域並找出其影響範圍非常重要。

MySQL 生態系統擁有許多工具,可以使這些任務變得更容易。檢視活躍程序和儲存引擎狀態,以及啟用和分析慢查詢日誌資訊,將為您提供所需的資訊,以針對成本最高的查詢。在我們的下一篇指南中,我們將討論如何實際最佳化您發現的查詢以及需要注意哪些事項以保持最佳效能。

關於作者
Justin Ellingwood

Justin Ellingwood

自 2013 年以來,Justin 一直在撰寫有關資料庫、Linux、基礎設施和開發者工具的文章。他目前與妻子和兩隻兔子住在柏林。他通常不需要以第三人稱寫作,這對所有相關方都是一種解脫。
© . This site is unofficial and not affiliated with Prisma Data, Inc.