MySQL預設資料庫之performance_schema庫

lhrbest發表於2019-07-21


MySQL預設資料庫之performance_schema庫


本系列文章所使用的資料庫版本為 MySQL 官方 5.7.17版本

|1、 什麼是performance_schema

MySQL的performance schema 用於監控MySQL server在一個較低階別的執行過程中的資源消耗、資源等待等情況,它具有以下特點:  

  1. 提供了一種在資料庫執行時實時檢查server的內部執行情況的方法。performance_schema 資料庫中的表使用performance_schema儲存引擎。該資料庫主要關注資料庫執行過程中的效能相關的資料,與information_schema不同,information_schema主要關注server執行過程中的後設資料資訊 

  2. performance_schema通過監視server的事件來實現監視server內部執行情況, “事件”就是server內部活動中所做的任何事情以及對應的時間消耗,利用這些資訊來判斷server中的相關資源消耗在了哪裡?一般來說,事件可以是函式呼叫、作業系統的等待、SQL語句執行的階段(如sql語句執行過程中的parsing 或 sorting階段)或者整個SQL語句與SQL語句集合。事件的採集可以方便的提供server中的相關儲存引擎對磁碟檔案、表I/O、表鎖等資源的同步呼叫資訊。  

  3. performance_schema中的事件與寫入二進位制日誌中的事件(描述資料修改的events)、事件計劃排程程式(這是一種儲存程式)的事件不同。performance_schema中的事件記錄的是server執行某些活動對某些資源的消耗、耗時、這些活動執行的次數等情況。  

  4. performance_schema中的事件只記錄在本地server的performance_schema中,其下的這些表中資料發生變化時不會被寫入binlog中,也不會通過複製機制被複制到其他server中。 

  5. 當前活躍事件、歷史事件和事件摘要相關的表中記錄的資訊。能提供某個事件的執行次數、使用時長。進而可用於分析某個特定執行緒、特定物件(如mutex或file)相關聯的活動。 

  6. PERFORMANCE_SCHEMA儲存引擎使用server原始碼中的“檢測點”來實現事件資料的收集。對於performance_schema實現機制本身的程式碼沒有相關的單獨執行緒來檢測,這與其他功能(如複製或事件計劃程式)不同 

  7. 收集的事件資料儲存在performance_schema資料庫的表中。這些表可以使用SELECT語句查詢,也可以使用SQL語句更新performance_schema資料庫中的表記錄(如動態修改performance_schema的setup_*開頭的幾個配置表,但要注意:配置表的更改會立即生效,這會影響資料收集)  

  8. performance_schema的表中的資料不會持久化儲存在磁碟中,而是儲存在記憶體中,一旦伺服器重啟,這些資料會丟失(包括配置表在內的整個performance_schema下的所有資料) 

  9. MySQL支援的所有平臺中事件監控功能都可用,但不同平臺中用於統計事件時間開銷的計時器型別可能會有所差異。 

performance_schema實現機制遵循以下設計目標:  

  1. 啟用performance_schema不會導致server的行為發生變化。例如,它不會改變執行緒排程機制,不會導致查詢執行計劃(如EXPLAIN)發生變化 

  2. 啟用performance_schema之後,server會持續不間斷地監測,開銷很小。不會導致server不可用 

  3. 在該實現機制中沒有增加新的關鍵字或語句,解析器不會變化  

  4. 即使performance_schema的監測機制在內部對某事件執行監測失敗,也不會影響server正常執行 

  5. 如果在開始收集事件資料時碰到有其他執行緒正在針對這些事件資訊進行查詢,那麼查詢會優先執行事件資料的收集,因為事件資料的收集是一個持續不斷的過程,而檢索(查詢)這些事件資料僅僅只是在需要檢視的時候才進行檢索。也可能某些事件資料永遠都不會去檢索 

  6. 需要很容易地新增新的instruments監測點 

  7. instruments(事件採集項)程式碼版本化:如果instruments的程式碼發生了變更,舊的instruments程式碼還可以繼續工作。 

  8. 注意:MySQL sys schema是一組物件(包括相關的檢視、儲存過程和函式),可以方便地訪問performance_schema收集的資料。同時檢索的資料可讀性也更高(例如:performance_schema中的時間單位是皮秒,經過sys schema查詢時會轉換為可讀的us,ms,s,min,hour,day等單位),sys schem在5.7.x版本預設安裝 

|2、 performance_schema使用快速入門

現在,是否覺得上面的介紹內容太過枯燥呢?如果你這麼想,那就對了,我當初學習的時候也是這麼想的。但現在,對於什麼是performance_schema這個問題上,比起更早之前更清晰了呢?如果你還沒有打算要放棄閱讀本文的話,那麼,請跟隨我們開始進入到"邊走邊唱"環節吧! 

2.1檢查當前資料庫版本是否支援

performance_schema被視為儲存引擎。 如果該引擎可用,則應該在INFORMATION_SCHEMA.ENGINES表或SHOW ENGINES語句的輸出中都可以看到它的SUPPORT值為YES,如下: 

使用 INFORMATION_SCHEMA.ENGINES表來查詢你的資料庫例項是否支援INFORMATION_SCHEMA引擎

qogir_env@localhost : performance_schema 02:41:41 > SELECT * FROM INFORMATION_SCHEMA.ENGINES WHERE ENGINE = 'PERFORMANCE_SCHEMA' ;
+--------------------+---------+--------------------+--------------+------+------------+
| ENGINE             | SUPPORT | COMMENT            | TRANSACTIONS | XA   | SAVEPOINTS |
+--------------------+---------+--------------------+--------------+------+------------+
|
PERFORMANCE_SCHEMA | YES     | Performance Schema | NO           | NO   | NO         |
+--------------------+---------+--------------------+--------------+------+------------+
1 row in set ( . 00 sec)

使用show命令來查詢你的資料庫例項是否支援INFORMATION_SCHEMA引擎

qogir_env@localhost : performance_schema 02:41:54 > show engines;
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
| Engine             | Support | Comment                                                        
|
Transactions | XA   | Savepoints |
+--------------------+---------+----------------------------------------------------------------+--------------+------+------------+
......
|
PERFORMANCE_SCHEMA | YES     | Performance Schema                                            
| NO           | NO   | NO         |
......
9 rows in set ( . 00 sec)

當我們看到PERFORMANCE_SCHEMA 對應的Support 欄位輸出為YES時就表示我們當前的資料庫版本是支援performance_schema的。但知道我們的例項支援performance_schema引擎就可以使用了嗎?NO,很遺憾,performance_schema在5.6及其之前的版本中,預設沒有啟用,從5.7及其之後的版本才修改為預設啟用。現在,我們來看看如何設定performance_schema預設啟用吧! 

2.2. 啟用performance_schema

從上文中我們已經知道,performance_schema在5.7.x及其以上版本中預設啟用(5.6.x及其以下版本預設關閉),如果要顯式啟用或關閉時,我們需要使用引數performance_schema=ON|OFF設定,並在my.cnf中進行配置:

[mysqld]
performance_schema = ON  # 注意:該引數為只讀引數,需要在例項啟動之前設定才生效

mysqld啟動之後,通過如下語句檢視performance_schema是否啟用生效(值為ON表示performance_schema已初始化成功且可以使用了。如果值為OFF表示在啟用performance_schema時發生某些錯誤。可以檢視錯誤日誌進行排查):

qogir_env@localhost : performance_schema 03:13:10 > SHOW VARIABLES LIKE 'performance_schema' ;
+--------------------+-------+
| Variable_name      | Value |
+--------------------+-------+
|
performance_schema | ON    |
+--------------------+-------+
1 row in set ( . 00 sec)

現在,你可以在performance_schema下使用show tables語句或者通過查詢 INFORMATION_SCHEMA.TABLES表中performance_schema引擎相關的後設資料來了解在performance_schema下存在著哪些表: 

通過從INFORMATION_SCHEMA.tables表查詢有哪些performance_schema引擎的表:

qogir_env@localhost : performance_schema 03:13:22 > SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES \
WHERE TABLE_SCHEMA = 'performance_schema' and engine= 'performance_schema' ;
+------------------------------------------------------+
| TABLE_NAME                                           |
+------------------------------------------------------+
| accounts                                             |
| cond_instances                                       |
......
| users                                                |
| variables_by_thread                                  |
+------------------------------------------------------+
87 rows in set ( . 00 sec)

直接在performance_schema庫下使用show tables語句來檢視有哪些performance_schema引擎表:

qogir_env@localhost : performance_schema 03:20:43 > use performance_schema
Database changed
qogir_env@localhost : performance_schema 03:21:06 > show tables from performance_schema;
+------------------------------------------------------+
| Tables_in_performance_schema                         |
+------------------------------------------------------+
| accounts                                             |
| cond_instances                                       |
......
| users                                                |
| variables_by_thread                                  |
+------------------------------------------------------+
87 rows in set ( . 00 sec)

現在,我們知道了在 MySQL 5.7.17 版本中,performance_schema 下一共有87張表,那麼,這87帳表都是存放什麼資料的呢?我們如何使用他們來查詢我們想要檢視的資料呢?先彆著急,我們先來看看這些表是如何分類的。 

2.3. performance_schema表的分類

performance_schema庫下的表可以按照監視不同的緯度進行了分組,例如:或按照不同資料庫物件進行分組,或按照不同的事件型別進行分組,或在按照事件型別分組之後,再進一步按照帳號、主機、程式、執行緒、使用者等,如下:

按照事件型別分組記錄效能事件資料的表 

語句事件記錄表,這些表記錄了語句事件資訊,當前語句事件表events_statements_current、歷史語句事件表events_statements_history和長語句歷史事件表events_statements_history_long、以及聚合後的摘要表summary,其中,summary表還可以根據帳號(account),主機(host),程式(program),執行緒(thread),使用者(user)和全域性(global)再進行細分)

qogir_env@localhost : performance_schema 03:51:36 > show tables like 'events_statement%' ;
+----------------------------------------------------+
| Tables_in_performance_schema (%statement%)         |
+----------------------------------------------------+
| events_statements_current                          |
| events_statements_history                          |
| events_statements_history_long                     |
| events_statements_summary_by_account_by_event_name |
| events_statements_summary_by_digest                |
| events_statements_summary_by_host_by_event_name    |
| events_statements_summary_by_program               |
| events_statements_summary_by_thread_by_event_name  |
| events_statements_summary_by_user_by_event_name    |
| events_statements_summary_global_by_event_name     |
+----------------------------------------------------+
11 rows in set ( . 00 sec)

等待事件記錄表,與語句事件型別的相關記錄表類似:

qogir_env@localhost : performance_schema 03:53:51 > show tables like 'events_wait%' ;
+-----------------------------------------------+
| Tables_in_performance_schema (%wait%)         |
+-----------------------------------------------+
| events_waits_current                          |
| events_waits_history                          |
| events_waits_history_long                     |
| events_waits_summary_by_account_by_event_name |
| events_waits_summary_by_host_by_event_name    |
| events_waits_summary_by_instance              |
| events_waits_summary_by_thread_by_event_name  |
| events_waits_summary_by_user_by_event_name    |
| events_waits_summary_global_by_event_name     |
+-----------------------------------------------+
12 rows in set ( . 01 sec)

階段事件記錄表,記錄語句執行的階段事件的表,與語句事件型別的相關記錄表類似:

qogir_env@localhost : performance_schema 03:55:07 > show tables like 'events_stage%' ;
+------------------------------------------------+
| Tables_in_performance_schema (%stage%)         |
+------------------------------------------------+
| events_stages_current                          |
| events_stages_history                          |
| events_stages_history_long                     |
| events_stages_summary_by_account_by_event_name |
| events_stages_summary_by_host_by_event_name    |
| events_stages_summary_by_thread_by_event_name  |
| events_stages_summary_by_user_by_event_name    |
| events_stages_summary_global_by_event_name     |
+------------------------------------------------+
8 rows in set ( . 00 sec)

事務事件記錄表,記錄事務相關的事件的表,與語句事件型別的相關記錄表類似:

qogir_env@localhost : performance_schema 03:55:30 > show tables like 'events_transaction%' ;
+------------------------------------------------------+
| Tables_in_performance_schema (%transaction%)         |
+------------------------------------------------------+
| events_transactions_current                          |
| events_transactions_history                          |
| events_transactions_history_long                     |
| events_transactions_summary_by_account_by_event_name |
| events_transactions_summary_by_host_by_event_name    |
| events_transactions_summary_by_thread_by_event_name  |
| events_transactions_summary_by_user_by_event_name    |
| events_transactions_summary_global_by_event_name     |
+------------------------------------------------------+
8 rows in set ( . 00 sec)

監視檔案系統層呼叫的表:

qogir_env@localhost : performance_schema 03:58:27 > show tables like '%file%' ;
+---------------------------------------+
| Tables_in_performance_schema (%file%) |
+---------------------------------------+
| file_instances                        |
| file_summary_by_event_name            |
| file_summary_by_instance              |
+---------------------------------------+
3 rows in set ( . 01 sec)

監視記憶體使用的表:

qogir_env@localhost : performance_schema 03:58:38 > show tables like '%memory%' ;
+-----------------------------------------+
| Tables_in_performance_schema (%memory%) |
+-----------------------------------------+
| memory_summary_by_account_by_event_name |
| memory_summary_by_host_by_event_name    |
| memory_summary_by_thread_by_event_name  |
| memory_summary_by_user_by_event_name    |
| memory_summary_global_by_event_name     |
+-----------------------------------------+
5 rows in set ( . 01 sec)

動態對performance_schema進行配置的配置表:

root@localhost : performance_schema 12:18:46 > show tables like '%setup%' ;
+----------------------------------------+
| Tables_in_performance_schema (%setup%) |
+----------------------------------------+
| setup_actors                           |
| setup_consumers                        |
| setup_instruments                      |
| setup_objects                          |
| setup_timers                           |
+----------------------------------------+
5 rows in set ( . 00 sec)

現在,我們已經大概知道了performance_schema中的主要表的分類,但,如何使用他們來為我們提供需要的效能事件資料呢?下面,我們介紹如何通過performance_schema下的配置表來配置與使用performance_schema。 

2.4. performance_schema簡單配置與使用

資料庫剛剛初始化並啟動時,並非所有instruments(事件採集項,在採集項的配置表中每一項都有一個開關欄位,或為YES,或為NO)和consumers(與採集項類似,也有一個對應的事件型別儲存表配置項,為YES就表示對應的表儲存效能資料,為NO就表示對應的表不儲存效能資料)都啟用了,所以預設不會收集所有的事件,可能你需要檢測的事件並沒有開啟,需要進行設定,可以使用如下兩個語句開啟對應的instruments和consumers(行計數可能會因MySQL版本而異),例如,我們以配置監測等待事件資料為例進行說明: 

開啟等待事件的採集器配置項開關,需要修改setup_instruments 配置表中對應的採集器配置項

qogir_env @ localhost : performance_schema 03 : 34 : 40 > UPDATE setup_instruments SET ENABLED = 'YES' , TIMED = 'YES' where name like 'wait%' ;;
Query OK , 0 rows affected (0 .00 sec )
Rows matched : 323   Changed : 0   Warnings : 0

開啟等待事件的儲存表配置開關,修改修改setup_consumers 配置表中對應的配置i向

qogir_env @ localhost : performance_schema 04 : 23 : 40 > UPDATE setup_consumers SET ENABLED = 'YES' where name like '%wait%' ;
Query OK , 3 rows affected (0 .04 sec )
Rows matched : 3   Changed : 3   Warnings : 0

配置好之後,我們就可以檢視server當前正在做什麼,可以通過查詢events_waits_current表來得知,該表中每個執行緒只包含一行資料,用於顯示每個執行緒的最新監視事件(正在做的事情):

qogir _env@localhost : performance_ schema 04:23:52> SELECT * FROM events _waits_ current limit 1\G
*************************** 1. row ***************************
       THREAD_ID: 4
        EVENT_ID: 60
    END_EVENT_ID: 60
      EVENT_NAME: wait/synch/mutex/innodb/log_sys_mutex
          SOURCE: log0log.cc:1572
     TIMER_START: 1582395491787124480
       TIMER_END: 1582395491787190144
      TIMER_WAIT: 65664
           SPINS: NULL
   OBJECT_SCHEMA: NULL
     OBJECT_NAME: NULL
      INDEX_NAME: NULL
     OBJECT_TYPE: NULL
OBJECT _INSTANCE_ BEGIN: 955681576
NESTING _EVENT_ ID: NULL
NESTING _EVENT_ TYPE: NULL
       OPERATION: lock
 NUMBER _OF_ BYTES: NULL
           FLAGS: NULL
1 row in set (0.02 sec)
# 該事件資訊表示執行緒ID為4的執行緒正在等待innodb儲存引擎的log_sys_mutex鎖,這是innodb儲存引擎的一個互斥鎖,等待時間為65664皮秒(*_ID列表示事件來自哪個執行緒、事件編號是多少;EVENT_NAME表示檢測到的具體的內容;SOURCE表示這個檢測程式碼在哪個原始檔中以及行號;計時器欄位TIMER_START、TIMER_END、TIMER_WAIT分別表示該事件的開始時間、結束時間、以及總的花費時間,如果該事件正在執行而沒有結束,那麼TIMER_END和TIMER_WAIT的值顯示為NULL。注:計時器統計的值是近似值,並不是完全精確)

_current表中每個執行緒只保留一條記錄,且一旦執行緒完成工作,該表中不會再記錄該執行緒的事件資訊,_history表中記錄每個執行緒已經執行完成的事件資訊,但每個執行緒的只事件資訊只記錄10條,再多就會被覆蓋掉,*_history_long表中記錄所有執行緒的事件資訊,但總記錄數量是10000行,超過會被覆蓋掉,現在我們們檢視一下歷史表events_waits_history 中記錄了什麼:

qogir_env@localhost : performance_schema 06:14: 08> SELECT THREAD_ID,EVENT_ID,EVENT_NAME,TIMER_WAIT FROM events_waits_history ORDER BY THREAD_ID limit 21 ;
+-----------+----------+------------------------------------------+------------+
| THREAD_ID | EVENT_ID | EVENT_NAME                               | TIMER_WAIT |
+-----------+----------+------------------------------------------+------------+
|
        4 |      341 | wait/synch/mutex/innodb/fil_system_mutex |      84816 |
|         4 |       342 | wait/synch/mutex/innodb/fil_system_mutex |       32832 |
|
        4 |      343 | wait/io/file/innodb/innodb_log_file       |  544126864 |
......
|         4 |       348 | wait/io/file/innodb/innodb_log_file      |   693076224 |
|
        4 |      349 | wait/synch/mutex/innodb/fil_system_mutex |      65664 |
|         4 |       350 | wait/synch/mutex/innodb/log_sys_mutex    |       25536 |
|
        13 |     2260 | wait/synch/mutex/innodb/buf_pool_mutex   |     111264 |
|        13 |     2259 | wait/synch/mutex/innodb/fil_system_mutex |     8708688 |
......
|
        13 |     2261 | wait/synch/mutex/innodb/flush_list_mutex |     122208 |
|        15 |       291 | wait/synch/mutex/innodb/buf_dblwr_mutex  |       37392 |
+-----------+----------+------------------------------------------+------------+
21 rows in set (0.00 sec)

summary表提供所有事件的彙總資訊。該組中的表以不同的方式彙總事件資料(如:按使用者,按主機,按執行緒等等)。例如:要檢視哪些instruments佔用最多的時間,可以通過對events_waits_summary_global_by_event_name表的COUNT_STAR或SUM_TIMER_WAIT列進行查詢(這兩列是對事件的記錄數執行COUNT(*)、事件記錄的TIMER_WAIT列執行SUM(TIMER_WAIT)統計而來),如下:

qogir_env@localhost : performance_schema 06:17:23 > SELECT EVENT_NAME,COUNT_STAR FROM events_waits_summary_global_by_event_name \
ORDER BY COUNT_STAR DESC LIMIT 10 ;
| EVENT_NAME                                        | COUNT_STAR |
+---------------------------------------------------+------------+
|
wait/synch/mutex/mysys/THR_LOCK_malloc             |       6419 |
| wait/io/file/sql/FRM                              |         452 |
|
wait/synch/mutex/sql/LOCK_plugin                   |        337 |
| wait/synch/mutex/mysys/THR_LOCK_open              |         187 |
|
wait/synch/mutex/mysys/LOCK_alarm                 |        147 |
| wait/synch/mutex/sql/THD::LOCK_thd_data           |         115 |
|
wait/io/file/myisam/kfile                         |        102 |
| wait/synch/mutex/sql/LOCK_global_system_variables |         89 |
|
wait/synch/mutex/mysys/THR_LOCK::mutex             |         89 |
| wait/synch/mutex/sql/LOCK_open                    |         88 |
+---------------------------------------------------+------------+
qogir_env@localhost : performance_schema 06:19:20> SELECT EVENT_NAME,SUM_TIMER_WAIT FROM events_waits_summary_global_by_event_name\
ORDER BY SUM_TIMER_WAIT DESC LIMIT 10;
+----------------------------------------+----------------+
|
EVENT_NAME                             | SUM_TIMER_WAIT |
+----------------------------------------+----------------+
| wait/io/file/sql/MYSQL_LOG             |     1599816582 |
|
wait/synch/mutex/mysys/THR_LOCK_malloc |     1530083250 |
| wait/io/file/sql/binlog_index          |     1385291934 |
|
wait/io/file/sql/FRM                   |     1292823243 |
| wait/io/file/myisam/kfile              |       411193611 |
|
wait/io/file/myisam/dfile               |      322401645 |
| wait/synch/mutex/mysys/LOCK_alarm      |       145126935 |
|
wait/io/file/sql/casetest               |      104324715 |
| wait/synch/mutex/sql/LOCK_plugin       |       86027823 |
|
wait/io/file/sql/pid                   |       72591750 |
+----------------------------------------+----------------+
# 這些結果表明,THR_LOCK_malloc互斥事件是最熱的。注:THR_LOCK_malloc互斥事件僅在DEBUG版本中存在,GA版本不存在  

instance表記錄了哪些型別的物件會被檢測。這些物件在被server使用時,在該表中將會產生一條事件記錄,例如,file_instances表列出了檔案I/O操作及其關聯檔名:

qogir_env@localhost : performance_schema 06:27:26 > SELECT * FROM file_instances limit 20 ;
+------------------------------------------------------+--------------------------------------+------------+
| FILE_NAME                                            | EVENT_NAME                           | OPEN_COUNT |
+------------------------------------------------------+--------------------------------------+------------+
| /home/mysql/program/share/english/errmsg.sys         | wait/io/file/sql/ERRMSG              
|          0 |
| /home/mysql/program/share/charsets/Index.xml         | wait/io/file/mysys/charset          
|          0 |
| /data/mysqldata1/innodb_ts/ibdata1                   | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/innodb_log/ib_logfile0              | wait/io/file/innodb/innodb_log_file   |          2 |
| /data/mysqldata1/innodb_log/ib_logfile1              | wait/io/file/innodb/innodb_log_file   |          2 |
| /data/mysqldata1/undo/undo001                        | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/undo/undo002                        | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/undo/undo003                        | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/undo/undo004                        | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/mydata/multi_master/test.ibd        | wait/io/file/innodb/innodb_data_file |          1 |
| /data/mysqldata1/mydata/mysql/engine_cost.ibd        | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/mydata/mysql/gtid_executed.ibd      | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/mydata/mysql/help_category.ibd      | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/mydata/mysql/help_keyword.ibd       | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/mydata/mysql/help_relation.ibd      | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/mydata/mysql/help_topic.ibd         | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/mydata/mysql/innodb_index_stats.ibd | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/mydata/mysql/innodb_table_stats.ibd | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/mydata/mysql/plugin.ibd             | wait/io/file/innodb/innodb_data_file |          3 |
| /data/mysqldata1/mydata/mysql/server_cost.ibd        | wait/io/file/innodb/innodb_data_file |          3 |
+------------------------------------------------------+--------------------------------------+------------+
20 rows in set ( . 00 sec)


本文小結

本篇內容到這裡就接近尾聲了,相信很多人都認為,我們大多數時候並不會直接使用performance_schema來查詢效能資料,而是使用sys schema下的檢視代替,為什麼不直接學習sys schema呢?那你知道sys schema中的資料是從哪裡吐出來的嗎?performance_schema 中的資料實際上主要是從performance_schema、information_schema中獲取,所以要想玩轉sys schema,全面瞭解performance_schema必不可少。另外,對於sys schema、informatiion_schema甚至是mysql schema,我們後續也會推出不同的系列文章分享給大家。 


將為大家全面講解performance_schema配置方式以及各個配置表的作用。下面,請跟隨我們一起開始performance_schema系統的學習之旅吧。



|  基本概念

instruments :生產者,用於採集MySQL 中各種各樣的操作產生的事件資訊,對應配置表中的配置項我們可以稱為監控採集配置項,以下提及生產者均統稱為instruments

consumers :消費者,對應的消費者表用於儲存來自instruments採集的資料,對應配置表中的配置項我們可以稱為消費儲存配置項,以下提及消費者均統稱為consumers



友情提示 :以下內容閱讀起來可能比較燒腦,內容也較長,建議大家端好板凳,坐下來,點上一支菸,細細品讀,這也是學習performance_schema路上不得不過的火焰山,堅持下去,"翻過這座山,你就可以看到一片海!"


 編譯時配置

在以往,我們認為自行編譯安裝MySQL其效能要優於官方編譯好的二進位制包、rpm包等。可能在MySQL早期的版本中有這樣的情況, 但隨著MySQL版本不斷迭代,業界不少人親測證實,目前的MySQL版本並不存在自行編譯安裝效能比官方編譯好的二進位制包效能高,所以,通常情況下,我們不建議去耗費數十分鐘來編譯安裝MySQL,因為在大規模部署的場景,此舉十分浪費時間(需要通過編譯安裝的方式精簡模組的場景除外) 

可以使用cmake的編譯選項來自行決定你的MySQL例項是否支援performance_schema的某個等待事件類別,如下:


shell> cmake . \

   -DDISABLE_PSI_STAGE=1 \   #關閉STAGE事件監視器
   -DDISABLE_PSI_STATEMENT=1   #關閉STATEMENT事件監視器

注意:雖然我們可以通過cmake的編譯選項關閉掉某些performance_schema的功能模組,但是,通常我們不建議這麼做,除非你非常清楚後續不可能使用到這些功能模組,否則後續想要使用被編譯時關閉的模組,還需要重新編譯。  

當我們接手一個別人安裝的MySQL資料庫伺服器時,或者你並不清楚自己安裝的MySQL版本是否支援performance_schema時,我們可以通過mysqld命令檢視是否支援Performance Schema



# 如果發現performance_schema開頭的幾個選項,則表示當前mysqld支援performance_schema,如果沒有發現performance_schema相關的選項,說明當前資料庫版本不支援performance_schema,你可能需要升級mysql版本:


shell> mysqld --verbose -- help
...
--performance_schema
                 Enable the performance schema.
--performance_schema_events_waits_history_long_size= #
                 Number of rows in events_waits_history_long.


還可以登入到 MySQL 例項中使用 SQL 命令檢視是否支援 performance_schema



# Support列值為YES表示資料庫支援,否則你可能需要升級mysql版本:


mysql> SHOW ENGINES\G
...
admin@localhost : (none) 12:54:00> show engines;
*************************** 6. row ***************************
 Engine: PERFORMANCE_SCHEMA
Support: YES
Comment: Performance Schema
Transactions: NO
     XA: NO
Savepoints: NO
9 rows in set (0.00 sec)

注意:在mysqld選項或show engines語句輸出的結果中,如果看到有performance_schema相關的資訊,並不代表已經啟用了performance_schema,僅僅只是代表資料庫支援,如果需要啟用它,還需要在伺服器啟動時使用系統引數performance_schema=on(MySQL 5.7之前的版本預設關閉)顯式開啟


|   啟動時配置

performance_schema中的配置是儲存在記憶體中的,是易失的,也就是說儲存在performance_schema配置表(本章後續內容會講到)中的配置項在MySQL例項停止時會全部丟失。所以,如果想要把配置項持久化,就需要在MySQL的配置檔案中使用啟動選項來持久化配置項,讓MySQL每次重啟都自動載入配置項,而不需要每次重啟都再重新配置。

(1)    啟動選項

performance_schema有哪些啟動選項呢?我們可以通過如下命令列命令進行檢視:


[root@localhost ~]

# mysqld --verbose --help |grep performance-schema |grep -v '\-\-' |sed '1d' |sed '/[0-9]\+/d'


......
performance-schema-consumer-events-stages-current FALSE
performance-schema-consumer-events-stages-history FALSE
performance-schema-consumer-events-stages-history- long FALSE
performance-schema-consumer-events-statements-current TRUE
performance-schema-consumer-events-statements-history TRUE
performance-schema-consumer-events-statements-history- long FALSE
performance-schema-consumer-events-transactions-current FALSE
performance-schema-consumer-events-transactions-history FALSE
performance-schema-consumer-events-transactions-history- long FALSE
performance-schema-consumer-events-waits-current FALSE
performance-schema-consumer-events-waits-history FALSE
performance-schema-consumer-events-waits-history- long FALSE
performance-schema-consumer-global-instrumentation TRUE
performance-schema-consumer-statements-digest TRUE
performance-schema-consumer-thread-instrumentation TRUE
performance-schema-instrument  
......

下面將對這些啟動選項進行簡單描述(這些啟動選項是用於指定consumers和instruments配置項在MySQL啟動時是否跟隨開啟的,之所以叫做啟動選項,是因為這些需要在mysqld啟動時就需要通過命令列指定或者需要在my.cnf中指定,啟動之後通過show variables命令無法檢視,因為他們不屬於system variables)

  • performance_schema_consumer_events_statements_current=TRUE

是否在mysql server啟動時就開啟events_statements_current表的記錄功能(該表記錄當前的語句事件資訊),啟動之後也可以在setup_consumers表中使用UPDATE語句進行動態更新setup_consumers配置表中的events_statements_current配置項,預設值為TRUE

  • performance_schema_consumer_events_statements_history=TRUE

performance_schema_consumer_events_statements_current 選項類似,但該選項是用於配置是否記錄語句事件短歷史資訊,預設為TRUE

  • performance_schema_consumer_events_stages_history_long=FALSE

performance_schema_consumer_events_statements_current 選項類似,但該選項是用於配置是否記錄語句事件長曆史資訊,預設為FALSE

  • 除了statement(語句)事件之外,還支援:wait(等待)事件、state(階段)事件、transaction(事務)事件,他們與statement事件一樣都有三個啟動項分別進行配置,但這些等待事件預設未啟用,如果需要在MySQL Server啟動時一同啟動,則通常需要寫進my.cnf配置檔案中

  • performance_schema_consumer_global_instrumentation=TRUE

是否在MySQL Server啟動時就開啟全域性表(如: mutex_instances、rwlock_instances、cond_instances、file_instances、users、hostsaccounts、socket_summary_by_event_name、file_summary_by_instance 等大部分的全域性物件計數統計和事件彙總統計資訊表 )的記錄功能,啟動之後也可以在setup_consumers表中使用UPDATE語句進行動態更新全域性配置項

預設值為TRUE

  • performance_schema_consumer_statements_digest=TRUE

是否在MySQL Server啟動時就開啟events_statements_summary_by_digest 表的記錄功能,啟動之後也可以在 setup_consumers 表中使用UPDATE語句進行動態更新digest配置項

預設值為TRUE

  • performance_schema_consumer_thread_instrumentation=TRUE

是否在MySQL Server啟動時就開啟

events_xxx_summary_by_yyy_by_event_name 表的記錄功能,啟動之後也可以在 setup_consumers 表中使用UPDATE語句進行動態更新執行緒配置項

預設值為TRUE

  • performance_schema_instrument[=name]

是否在MySQL Server啟動時就啟用某些採集器,由於instruments配置項多達數千個,所以該配置項支援key-value模式,還支援%號進行通配等,如下:



# [=name]可以指定為具體的Instruments名稱(但是這樣如果有多個需要指定的時候,就需要使用該選項多次),也可以使用萬用字元,可以指定instruments相同的字首+萬用字元,也可以使用%代表所有的instruments


## 指定開啟單個instruments
--performance-schema-instrument = 'instrument_name=value'
## 使用萬用字元指定開啟多個instruments
--performance-schema-instrument = 'wait/synch/cond/%=COUNTED'
## 開關所有的instruments
--performance-schema-instrument = '%=ON'
--performance-schema-instrument = '%=OFF'


注意,這些啟動選項要生效的前提是,需要設定performance_schema=ON。另外,這些啟動選項雖然無法使用show variables語句檢視,但我們可以通過setup_instruments和setup_consumers表查詢這些選項指定的值。

(2)  system variables

與performance_schema相關的system variables可以使用如下語句檢視,這些variables用於限定consumers表的儲存限制,它們都是隻讀變數,需要在MySQL啟動之前就設定好這些變數的值。



root

@

localhost

 : (none) 

11

:

43

:

29

> show variables like 

'%performance_schema%'

;

.....
42 rows in set (0 .01 sec )


下面,我們將對這些system variables(以下稱為變數)中幾個需要關注的進行簡單解釋(其中大部分變數是-1值,代表會自動調整,無需太多關注,另外,大於-1值的變數在大多數時候也夠用,如果無特殊需求,不建議調整,調整這些引數會增加記憶體使用量)

  • performance_schema=ON

    • 控制performance_schema功能的開關,要使用MySQL的performance_schema,需要在mysqld啟動時啟用,以啟用事件收集功能

    • 該引數在5.7.x之前支援 performance_schema 的版本中預設關閉,5.7.x版本開始預設開啟

    • 注意:如果mysqld在初始化 performance_schema 時發現無法分配任何相關的內部緩衝區,則 performance_schema 將自動禁用,並將 performance_schema 設定為OFF

  • performance_schema_digests_size=10000

    • 控制 events_statements_summary_by_digest 表中的最大行數。如果產生的語句摘要資訊超過此最大值,便無法繼續存入該表,此時 performance_schema 會增加狀態變數

  • performance_schema_events_statements_history_long_size=10000

    • 控制 events_statements_history_long 表中的最大行數,該引數控制所有會話在 events_statements_history_long 表中能夠存放的總事件記錄數,超過這個限制之後,最早的記錄將被覆蓋

    • 全域性變數,只讀變數,整型值,5.6.3版本引入 
      * 5.6.x版本中,5.6.5及其之前的版本預設為10000,5.6.6及其之後的版本預設值為-1,通常情況下,自動計算的值都是10000 
      * 5.7.x版本中,預設值為-1,通常情況下,自動計算的值都是10000

  • performance_schema_events_statements_history_size=10

    • 控制 events_statements_history 表中單個執行緒(會話)的最大行數,該引數控制單個會話在 events_statements_history 表中能夠存放的事件記錄數,超過這個限制之後,單個會話最早的記錄將被覆蓋

    • 全域性變數,只讀變數,整型值,5.6.3版本引入 
      * 5.6.x版本中,5.6.5及其之前的版本預設為10,5.6.6及其之後的版本預設值為-1,通常情況下,自動計算的值都是10 
      * 5.7.x版本中,預設值為-1,通常情況下,自動計算的值都是10

  • 除了statement(語句)事件之外,wait(等待)事件、state(階段)事件、transaction(事務)事件,他們與statement事件一樣都有三個引數分別進行儲存限制配置,有興趣的同學自行研究,這裡不再贅述

  • performance_schema_max_digest_length=1024

    • 用於控制標準化形式的SQL語句文字在存入 performance_schema 時的限制長度,該變數與 max_digest_length 變數相關( max_digest_length 變數含義請自行查閱相關資料)

    • 全域性變數,只讀變數,預設值1024位元組,整型值,取值範圍0~1048576,5.6.26和5.7.8版本中引入

  • performance_schema_max_sql_text_length=1024

    • 控制存入 events_statements_current,events_statements_history events_statements_history_long 語句事件表中的 SQL_TEXT 列的最大SQL長度位元組數。 超出系統變數 performance_schema_max_sql_text_length 的部分將被丟棄,不會記錄,一般情況下不需要調整該引數,除非被截斷的部分與其他SQL比起來有很大差異

    • 全域性變數,只讀變數,整型值,預設值為1024位元組,取值範圍為0~1048576,5.7.6版本引入

    • 降低系統變數 performance_schema_max_sql_text_length 值可以減少記憶體使用,但如果彙總的SQL中,被截斷部分有較大差異,會導致沒有辦法再對這些有較大差異的SQL進行區分。 增加該系統變數值會增加記憶體使用,但對於彙總SQL來講可以更精準地區分不同的部分。



|   執行時配置

在MySQL啟動之後,我們就無法使用啟動選項來開關相應的consumers和instruments了,此時,我們如何根據自己的需求來靈活地開關performance_schema中的採集資訊呢?(例如:預設配置下很多配置項並未開啟,我們可能需要即時去修改配置,再如:高併發場景,大量的執行緒連線到MySQL,執行各種各樣的SQL時產生大量的事件資訊,而我們只想看某一個會話產生的事件資訊時,也可能需要即時去修改配置),我們可以通過修改performance_schema下的幾張配置表中的配置項實現

這些配置表中的配置項之間存在著關聯關係,按照配置影響的先後順序,可整理為如下圖(該表僅代表個人理解): 

MySQL預設資料庫之performance_schema庫


(1)   performance_timers表

performance_timers表中記錄了server中有哪些可用的事件計時器(注意:該表中的配置項不支援增刪改,是隻讀的。有哪些計時器就表示當前的版本支援哪些計時器),setup_timers配置表中的配置項引用此表中的計時器

每個計時器的精度和數量相關的特徵值會有所不同,可以通過如下查詢語句檢視performance_timers表中記錄的計時器和相關的特徵資訊:


mysql> SELECT * FROM performance_timers;

+-------------+-----------------+------------------+----------------+
| TIMER_NAME | TIMER_FREQUENCY | TIMER_RESOLUTION | TIMER_OVERHEAD |
+-------------+-----------------+------------------+----------------+
|
CYCLE | 2389029850 | 1 | 72 |
| NANOSECOND | 1000000000 | 1 | 112 |
|
MICROSECOND | 1000000 | 1 | 136 |
| MILLISECOND | 1036 | 1 | 168 |
|
TICK | 105 | 1 | 2416 |
+-------------+-----------------+------------------+----------------+


performance_timers 表中的欄位含義如下

  • TIMER_NAME:表示可用計時器名稱, CYCLE 是指基於 CPU (處理器)週期計數器的定時器。在 setup_timers 表中可以使用 performance_timers 表中列值不為null的計時器(如果 performance_timers 表中有某欄位值為 NULL ,則表示該定時器可能不支援當前 server 所在平臺)

  • TIMER_FREQUENCY:表示每秒鐘對應的計時器單位的數量(即,相對於每秒時間換算為對應的計時器單位之後的數值,例如:每秒= 1000 毫秒= 1000000 微秒= 1000000000 納秒)。對於 CYCLE 計時器的換算值,通常與 CPU 的頻率相關。對於 performance_timers 表中檢視到的 CYCLE 計時器的 TIMER_FREQUENCY 列值 ,是根據 2.4GHz 處理器的系統上獲得的預設值(在 2.4GHz 處理器的系統上, CYCLE 可能接近 2400000000 )。 NANOSECOND 、MICROSECOND 、MILLISECOND  計時器是基於固定的1秒換算而來。對於 TICK 計時器, TIMER_FREQUENCY 列值可能會因平臺而異(例如,某些平臺使用100個tick/秒,某些平臺使用1000個tick/秒)

  • TIMER_RESOLUTION:計時器精度值,表示在每個計時器被呼叫時額外增加的值(即使用該計時器時,計時器被呼叫一次,需要額外增加的值)。如果計時器的解析度為10,則其計時器的時間值在計時器每次被呼叫時,相當於TIMER_FREQUENCY值+10

  • TIMER_OVERHEAD:表示在使用定時器獲取事件時開銷的最小週期值( performance_schema 在初始化期間呼叫計時器20次,選擇一個最小值作為此欄位值),每個事件的時間開銷值是計時器顯示值的兩倍,因為在事件的開始和結束時都呼叫計時器。注意:計時器程式碼僅用於支援計時事件,對於非計時類事件(如呼叫次數的統計事件),這種計時器統計開銷方法不適用

  • PS:對於 performance_timers 表,不允許使用 TRUNCATE TABLE 語句

(2)   setup_timers

setup_timers表中記錄當前使用的事件計時器資訊(注意:該表不支援增加和刪除記錄,只支援修改和查詢)

可以通過 UPDATE 語句來更改 setup_timers.TIMER_NAME 列值,以給不同的事件類別選擇不同的計時器, setup_timers.TIMER_NAME 列有效值來自 performance_timers.TIMER_NAME 列值。

setup_timers 表的修改會立即影響監控。正在執行的事件可能會使用修改之前的計時器作為開始時間,但可能會使用修改之後的新的計時器作為結束時間,為了避免計時器更改後可能產生時間資訊收集到不可預測的結果,請在修改之後使用 TRUNCATE TABLE 語句來重置 performance_schema 中相關表中的統計資訊。


mysql> SELECT * FROM setup_timers;

+-------------+-------------+
| NAME        | TIMER_NAME   |
+-------------+-------------+
|
idle         | MICROSECOND |
| wait        | CYCLE       |
|
stage       | NANOSECOND  |
| statement  | NANOSECOND   |
|
transaction | NANOSECOND  |
+-------------+-------------+


setup_timers表欄位含義如下:

  • NAME:計時器型別,對應著某個事件類別(事件類別詳見 3.3.4 節)

  • TIMER_NAME :計時器型別名稱。此列可以修改,有效值參見 performance_timers.TIMER_NAME 列值

  • PS:對於 setup_timers 表,不允許使用 TRUNCATE TABLE 語句

(3)  setup_consumers

setup_consumers表列出了 consumers 可配置列表項(注意:該表不支援增加和刪除記錄,只支援修改和查詢),如下:


mysql> SELECT * FROM setup_consumers;

+----------------------------------+---------+
| NAME                            | ENABLED |
+----------------------------------+---------+
|
events_stages_current             | NO      |
| events_stages_history            | NO       |
|
events_stages_history_long       | NO      |
| events_statements_current        | YES     |
|
events_statements_history         | YES    |
| events_statements_history_long  | NO       |
|
events_transactions_current       | NO      |
| events_transactions_history      | NO       |
|
events_transactions_history_long | NO      |
| events_waits_current            | NO       |
|
events_waits_history             | NO      |
| events_waits_history_long        | NO       |
|
global_instrumentation           | YES    |
| thread_instrumentation          | YES     |
|
statements_digest                 | YES    |
+----------------------------------+---------+


setup_consumers 表的修改會立即影響監控, setup_consumers 欄位含義如下:

  • NAME:consumers配置名稱

  • ENABLED:consumers是否啟用,有效值為YES或NO,此列可以使用UPDATE語句修改。如果需要禁用consumers就設定為NO,設定為NO時,server不會維護這些consumers表的內容新增和刪除,且也會關閉consumers對應的instruments(如果沒有instruments發現採集資料沒有任何consumers消費的話)

  • PS:對於setup_consumers表,不允許使用TRUNCATE TABLE語句

setup_consumers 表中的 consumers 配置項具有層級關係,具有從較高階別到較低階別的層次結構,按照優先順序順序,可列舉為如下層次結構(你可以根據這個層次結構,關閉你可能不需要的較低階別的 consumers ,這樣有助於節省效能開銷,且後續檢視採集的事件資訊時也方便進行篩選): 

MySQL預設資料庫之performance_schema庫

從上圖中的資訊中可以看到, setup_consumers 表中 consumers 配置層次結構中:

  • global_instrumentation 處於頂級位置,優先順序最高。 
    * 當 global_instrumentation YES 時,會檢查 setup_consumers 表中的 statements_digest thread_instrumentation 的配置,會附帶檢查 setup_instruments、setup_objects、setup_timers 配置表 
    * 當 global_instrumentation YES 時(無論 setup_consumers 表中的 statements_digest thread_instrumentation 如何配置,只依賴於 global_instrumentation 的配置),會維護全域性 events 輸出表: mutex_instances、rwlock_instances、cond_instances、file_instances、users、hostsaccounts、socket_summary_by_event_name、file_summary_by_instance、file_summary_by_event_name、objects_summary_global_by_type、memory_summary_global_by_event_name、table_lock_waits_summary_by_table、table_io_waits_summary_by_index_usage、table_io_waits_summary_by_table、events_waits_summary_by_instance、events_waits_summary_global_by_event_name、events_stages_summary_global_by_event_name、events_statements_summary_global_by_event_name、events_transactions_summary_global_by_event_name 
    * 當 global_instrumentation NO 時,不會檢查任何更低階別的 consumers 配置,不會維護任何 events 輸出表( memory_% 開頭的events輸出表除外,這些表維護只受 setup_instruments 配置表控制)  

  • statements_digest thread_instrumentation 處於同一級別,優先順序次於 global_instrumentation ,且依賴於 global_instrumentation YES 時配置才會被檢測 
    * 當 statements_digest YES 時, statements_digest consumers 沒有更低階別的配置,依賴於 global_instrumentation YES 時配置才會被檢測,會維護 events 輸出表: events_statements_summary_by_digest 
    * 當 statements_digest NO 時,不維護 events 輸出表: events_statements_summary_by_digest 
    * 當 thread_instrumentation YES 時,會檢查 setup_consumers 表中的 events_xxx_current 配置( xxx 表示: waits、stages、statements、transactions ),會附帶檢查 setup_actors、threads 配置表。會維護 events 輸出表  events_xxx_summary_by_yyy_by_event_name ,其中:  xxx 含義同上;  yyy 表示: thread、user、host、account 
    * 當 thread_instrumentation NO 時,不檢查 setup_consumers 表中的 events_xxx_current 配置,不維護 events_xxx_current 及其更低階別的 events 輸出表

  • events_xxx_current 系列( xxx 含義同上) consumers 處於同一級別。且依賴於 thread_instrumentation YES 時配置才會被檢測 
    * 當 events_xxx_current YES 時,會檢測 setup_consumers 配置表中的 events_xxx_history events_xxx_history_long 系列  consumers 配置,會維護 events_xxx_current 系列表 
    * 當 events_xxx_current NO 時,不檢測 setup_consumers 配置表中的 events_xxx_history events_xxx_history_long 系列  consumers 配置,不維護 events_xxx_current 系列表

  • events_xxx_history events_xxx_history_long 系列(同 events_xxx_current 中的 xxx consumers 處於同一級別,優先順序次於 events_xxx_current  系列 consumers(xxx 含義同上),依賴於 events_xxx_current  系列 consumers YES 時才會被檢測 
    * 當 events_xxx_history YES 時,沒有更低階別的 conosumers 配置需要檢測,但會附帶檢測 setup_actors、threads 配置表中的 HISTORY 列值,會維護 events_xxx_history 系列表,反之不維護 
    * 當 events_xxx_history_long YES 時,沒有更低階別的 conosumers 配置需要檢測,但會附帶檢測 setup_actors、threads 配置表中的 HISTORY 列值,會維護 events_xxx_history_long 系列表,反之不維護

注意:

  • events  輸出表

    events_xxx_summary_by_yyy_by_event_name 的開關由 global_instrumentation 控制,且表中是有固定資料行,不可清理, truncate 或者關閉相關的 consumers 時只是不統計相關的 instruments 收集的 events 資料,相關欄位為0值

  • 如果 performance_schema 在對 setup_consumers 表做檢查時發現某個 consumers 配置行的 ENABLED  列值不為 YES ,則與這個 consumers 相關聯的 events 輸出表中就不會接收儲存任何事件記錄

  • 高階別的 consumers 設定不為 YES 時,依賴於這個 consumers 配置為 YES 時才會啟用的那些更低階別的 consumers 將一同被禁用

配置項修改示例:



#


 開啟events_waits_current表當前等待事件記錄功能


mysql> UPDATE setup_consumers SET ENABLED = 'NO' WHERE NAME = 'events_waits_current' ;
# 關閉歷史事件記錄功能
mysql> UPDATE setup_consumers SET ENABLED = 'NO' where name like '%history%' ;
# where 條件 ENABLED = 'YES' 即為開啟對應的記錄表功能
......

(4)  setup_instruments

setup_instruments 表列出了 instruments  列表配置項,即代表了哪些事件支援被收集:


mysql> SELECT * FROM setup_instruments;

+------------------------------------------------------------+---------+-------+
| NAME                                                      | ENABLED | TIMED |
+------------------------------------------------------------+---------+-------+
...
| wait/synch/mutex/sql/LOCK_global_read_lock                | YES     | YES  |
| wait/synch/mutex/sql/LOCK_global_system_variables          | YES     | YES  |
| wait/synch/mutex/sql/LOCK_lock_db                          | YES     | YES  |
| wait/synch/mutex/sql/LOCK_manager                          | YES     | YES  |
...
| wait/synch/rwlock/sql/LOCK_grant                          | YES     | YES  |
| wait/synch/rwlock/sql/LOGGER::LOCK_logger                  | YES     | YES  |
| wait/synch/rwlock/sql/LOCK_sys_init_connect                | YES     | YES  |
| wait/synch/rwlock/sql/LOCK_sys_init_slave                  | YES     | YES  |
...
| wait/io/file/sql/binlog                                    | YES     | YES  |
| wait/io/file/sql/binlog_index                              | YES     | YES  |
| wait/io/file/sql/casetest                                  | YES     | YES  |
| wait/io/file/sql/dbopt                                    | YES     | YES  |
...

instruments具有樹形結構的名稱空間,從 setup_instruments 表中的 NAME 欄位上可以看到, instruments 名稱的組成從左到右,最左邊的是頂層 instruments 型別命名,最右邊是一個具體的 instruments 名稱,有一些頂層 instruments 沒有其他層級的元件(如 :transaction idle ,那麼這個頂層型別既是型別又是具體的 instruments ),有一些頂層 instruments 具有下層 instruments (如: wait/io/file/myisam/log ),一個層級的 instruments 名稱對應的元件數量取決於 instruments 的型別。

一個給定 instruments 名稱的含義,需要看 instruments 名稱的左側命名而定,例如下邊兩個 myisam 相關名稱的 instruments 含義各不相同: 
名稱中給定元件的解釋取決於其左側的元件。例如, myisam 顯示在以下兩個名稱:



# 第一種instruments表示myisam引擎的檔案IO相關的instruments


wait /io/file/myisam/ log
# 第二種instruments表示myisam引擎的磁碟同步相關的instruments
wait /synch/cond/myisam/MI_SORT_INFO::cond

instruments的命名格式組成: performance_schema 實現的一個字首結構(如 :wait/io/file/myisam/log 中的 wait+ 由開發人員實現的 instruments 程式碼定義的一個字尾名稱組成(如: wait/io/file/myisam/log 中的 io/file/myisam/log

  • instruments名稱字首表示instruments的型別(如wait/io/file/myisam/log中的wait),該字首名稱還用於在setup_timers表中配置某個事件型別的定時器,也被稱作頂層元件

  • instruments名稱字尾部分來自instruments本身的程式碼。字尾可能包括以下層級的元件: 
    * 主要元件的名稱(如:myisam,innodb,mysys或sql,這些都是server的子系統模組元件)或外掛名稱 
    * 程式碼中變數的名稱,格式為XXX(全域性變數)或CCC::MMM(CCC表示一個類名,MMM表示在類CCC作用域中的一個成員物件),如: 'wait/synch/cond/sql/COND_thread_cache' instruments中的COND_thread_cache,'wait/synch/mutex/mysys/THR_LOCK_myisam' instruments中的THR_LOCK_myisam,'wait/synch/mutex/sql/MYSQL_BIN_LOG::LOCK_index' instruments中的MYSQL_BIN_LOG::LOCK_index

在原始碼中每一個實現的instruments,如果該原始碼被載入到server中,那麼在該表中就會有一行對應的配置,當啟用或執行instruments時,會建立對應的instruments例項,這些例項在* _instances表中可以檢視到

大多數setup_instruments配置行修改會立即影響監控,但對於某些instruments,執行時修改不生效(配置表可以修改,但不生效),只有在啟動之前修改才會生效(使用system variables寫到配置檔案中),不生效的instruments主要有mutexes, conditions, and rwlocks

setup_instruments表欄位詳解如下:

  • NAME:instruments名稱,instruments名稱可能具有多個部分並形成層次結構(詳見下文)。當instruments被執行時,產生的事件名稱就取自instruments的名稱,事件沒有真正的名稱,直接使用instruments來作為事件的名稱,可以將instruments與產生的事件進行關聯

  • ENABLED:instrumetns是否啟用,有效值為YES或NO,此列可以使用UPDATE語句修改。如果設定為NO,則這個instruments不會被執行,不會產生任何的事件資訊

  • TIMED:instruments是否收集時間資訊,有效值為YES或NO,此列可以使用UPDATE語句修改,如果設定為NO,則這個instruments不會收集時間資訊

對於記憶體instruments,setup_instruments中的TIMED列將被忽略(使用update語句對這些記憶體instruments設定timed列為YES時可以執行成功,但是你會發現執行update之後select這些instruments的timed列還是NO),因為記憶體操作沒有定時器資訊

如果某個instruments的enabled設定為YES(表示啟用這個instruments),但是timed列未設定為YES(表示計時器功能禁用),則instruments會產生事件資訊,但是事件資訊對應的TIMER_START,TIMER_END和TIMER_WAIT定時器值都為NULL。後續彙總表中計算sum,minimum,maximum和average時間值時會忽略這些null值

PS:setup_instruments表不允許使用TRUNCATE TABLE語句

setup_instruments中的instruments name層級結構圖如下:

MySQL預設資料庫之performance_schema庫


在setup_instruments表中的instruments頂級instruments 元件分類如下:

  • Idle Instrument 元件:用於檢測空閒事件的instruments,該instruments沒有其他層級的元件,空閒事件收集時機如下: 
    * 依據socket_instances表中的STATE欄位而定,STATE欄位有ACTIVE和IDLE兩個值,如果STATE欄位值為ACTIVE,則performance_schema使用與socket型別相對應的instruments跟蹤活躍的socket連線的等待時間(監聽活躍的socket的instruments有wait/io/socket/sql/server_tcpip_socket、wait/io/socket/sql/server_unix_socket、wait/io/socket/sql/client_connection),如果STATE欄位值為IDLE,則performance_schema使用idle instruments跟蹤空閒socket連線的等待時間 
    * 如果socket連線在等待來自客戶端的請求,則此時套接字處於空閒狀態,socket_instances表中處於空閒的套接字行的STATE欄位會從ACTIVE變為IDLE。 EVENT_NAME列值保持不變,instruments的定時器被暫停。 並在events_waits_current表中生成一個EVENT_NAME值為idle的事件記錄行 
    * 當套接字接收到客戶端的下一個請求時,空閒事件被終止,套接字例項從空閒狀態切換到活動狀態,並恢復套接字instruments的定時器工作 
    * socket_instances表不允許使用TRUNCATE TABLE語句 
    * 表欄位含義詳見後續socket_instances表介紹章節

  • transaction instrument 元件:用於檢測transactions 事件的instruments,該instruments沒有其他層級的元件

  • Memory Instrument 元件:用於檢測memorys 事件的instruments 
    * 預設情況下禁用了大多數memory instruments,但可以在server啟動時在my.cnf中啟用或禁用,或者在執行時更新setup_instruments表中相關instruments配置來動態啟用或禁用。memory instruments的命名格式為:memory/code_area/instrument_name,其中code_area是一個server元件字串值(如:sql、client、vio、mysys、partition和儲存引擎名稱:performance_schema、myisam、innodb、csv、myisammrg、memory、blackhole、archive等),而instrument_name是具體的instruments名稱 
    * 以字首'memory/performance_schema'命名的instruments顯示為performance_schem內部緩衝區分配了多少記憶體。'memory/performance_schema' 開頭的instruments'是內建的,無法在啟動時或者執行時人為開關,內部始終啟用。這些instruments採集的events事件記錄僅儲存在memory_summary_global_by_event_name表中。詳細資訊詳見後續章節

  • Stage Instrument 元件:用於檢測stages事件的instruments 
    * stage instruments命名格式為:'stage/code_area/stage_name' 格式,其中code_area是一個server元件字串值(與memory instruments類似),stage_name表示語句的執行階段,如'Sorting result' 和 'Sending data'。這些執行階段字串值與SHOW PROCESSLIST的State列值、INFORMATION_SCHEMA.PROCESSLIST表的STATE列值類似。

  • Statement Instrument 元件:用於檢測statements事件的instruments,包含如下幾個子類 
    * statement/abstract/ statement操作的抽象 instruments。抽象 instruments用於語句沒有確定語句型別的早期階段,在語句型別確定之後使用對應語句型別的instruments代替,詳細資訊見後續章節 
    * statement/com/:command操作相關的instruments。這些名稱對應於COM_xxx操作命令(詳見mysql_com.h標頭檔案和sql/sql_parse.cc檔案。例如:statement/com/Connect和statement/com/Init DB instruments分別對應於COM_CONNECT和COM_INIT_DB命令) 
    * statement/scheduler/event:用於跟蹤一個事件排程器執行過程中的所有事件的instruments,該型別instruments只有一個 
    * statement/sp/ 用於檢測儲存程式執行過程中的內部命令的instruemnts,例如,statement/sp/cfetch和statement/sp/freturn instruments表示檢測儲存程式內部使用遊標提取資料、函式返回資料等相關命令 
    statement/sql/:SQL語句操作相關的instruments。例如,statements/sql/create_db和statement/sql/select instruments,表示檢測CREATE DATABASE和SELECT語句的instruments

  • Wait Instrument 元件:用於檢測waits事件的instruments,包含如下幾個子類 
    * wait/io:用於檢測I/O操作的instruments,包含如下幾個子類 
    * 1)、wait/io/file:用於檢測檔案I/O操作的instruments,對於檔案來說,表示等待檔案相關的系統呼叫完成,如fwrite()系統呼叫。由於快取的存在,在資料庫中的相關操作時不一定需要在磁碟上做讀寫操作。 
    * 2)、wait/io/socket:用於檢測socket操作的instruments,socket instruments的命名形式為:'wait/io/socket/sql/socket_type',server在支援的每一種網路通訊協議上監聽socket。socket instruments監聽TCP/IP、Unix套接字檔案連線的socket_type有server_tcpip_socket、server_unix_socket值。當監聽套接字檢測到有客戶端連線進來時,server將客戶端連線轉移到被單獨執行緒管理的新套接字來處理。新連線執行緒對應的socket_type值為client_connection。使用語句select * from setup_instruments where name like 'wait/io/socket%';可以查詢這三個socket_type對應的instruments

wait/io/table/sql/handler: 

1). 表I/O操作相關的instruments。這個類別包括了對持久基表或臨時表的行級訪問(對資料行獲取,插入,更新和刪除),對於檢視來說,instruments檢測時會參照被檢視引用的基表訪問情況 

2). 與大多數等待事件不同,表I/O等待可以包括其他等待。例如,表I/O可能包括檔案I/O或記憶體操作。因此,表I/O等待的事件在events_waits_current表中的記錄通常有兩行(除了wait/io/table/sql/handler的事件記錄之外,可能還包含一行wait/io/file/myisam/dfile的事件記錄)。這種可以叫做表IO操作的原子事件

3). 某些行操作可能會導致多個表I/O等待。例如,如果有INSERT的觸發器,那麼插入操作可能導致觸發器更新操作。

wait/lock:鎖操作相關的instruments 

1). wait/lock/table:表鎖操作相關的instruments 

2). wait/lock/metadata/sql/mdl:MDL鎖操作相關的instruments

wait/synch :磁碟同步object相關的instruments, performance_schema.events_waits_xxx表中的TIMER_WAIT時間列包括了在嘗試獲取某個object上的鎖(如果這個物件上已經存在鎖)的時候被阻塞的時長。

1). wait/synch/cond:一個執行緒使用一個狀態來向其他執行緒發訊號通知他們正在等待的事情已經發生了。如果一個執行緒正在等待這個狀態,那麼它可以被這個狀態喚醒並繼續往下執行。如果是幾個執行緒正在等待這個狀態,則這些執行緒都會被喚醒,並競爭他們正在等待的資源,該instruments用於採集某執行緒等待這個資源時被阻塞的事件資訊。 

2). wait/synch/mutex:一個執行緒在訪問某個資源時,使用互斥物件防止其他執行緒同時訪問這個資源。該instruments用於採集發生互斥時的事件資訊 

3). wait/synch/rwlock:一個執行緒使用一個讀寫鎖物件對某個特定變數進行鎖定,以防止其他執行緒同時訪問,對於使用共享讀鎖鎖定的資源,多個執行緒可以同時訪問,對於使用獨佔寫鎖鎖定的資源,只有一個執行緒能同時訪問,該instruments用於採集發生讀寫鎖鎖定時的事件資訊 

4). wait/synch/sxlock:shared-exclusive(SX)鎖是一種rwlock鎖 object,它提供對公共資源的寫訪問的同時允許其他執行緒的不一致讀取。sxlocks鎖object可用於優化資料庫讀寫場景下的併發性和可擴充套件性。

要控制這些instruments的起停,將ENABLED列設定為YES或NO,要配置instruments是否收集計時器資訊,將TIMED列值設定為YES或NO

setup_instruments表,對大多數instruments的修改會立即影響監控。但對於某些instruments,修改需要在mysql server重啟才生效,執行時修改不生效。因為這些可能會影響mutexes、conditions和rwlocks,下面我們來看一些setup_instruments表修改示例:



#


 禁用所有instruments,修改之後,生效的instruments修改會立即產生影響,即立即關閉收集功能:


mysql> UPDATE setup_instruments SET ENABLED = 'NO' ;
# 禁用所有檔案類instruments,使用NAME欄位結合like模糊匹配:
mysql> UPDATE setup_instruments SET ENABLED = 'NO' WHERE NAME LIKE 'wait/io/file/%' ;
# 僅禁用檔案類instruments,啟用所有其他instruments,使用NAME欄位結合 if 函式,LIKE模糊匹配到就改為NO,沒有匹配到的就改為YES:
mysql> UPDATE setup_instruments SET ENABLED = IF(NAME LIKE 'wait/io/file/%' , 'NO' , 'YES' );
# 啟用所有型別的events的mysys子系統的instruments:
mysql> UPDATE setup_instruments SET ENABLED = CASE WHEN NAME LIKE '%/mysys/%' THEN 'YES' ELSE 'NO' END;
# 禁用指定的某一個instruments:
mysql> UPDATE setup_instruments SET ENABLED = 'NO' WHERE NAME = 'wait/synch/mutex/mysys/TMPDIR_mutex' ;
# 切換instruments開關的狀態,“翻轉”ENABLED值,使用ENABLED欄位值+ if 函式, IF(ENABLED = 'YES' , 'NO' , 'YES' )表示,如果ENABLED值為YES,則修改為NO,否則修改為YES:
mysql> UPDATE setup_instruments SET ENABLED = IF(ENABLED = 'YES' , 'NO' , 'YES' ) WHERE NAME = 'wait/synch/mutex/mysys/TMPDIR_mutex' ;
# 禁用所有instruments的計時器:
mysql> UPDATE setup_instruments SET TIMED = 'NO' ;

查詢innodb儲存引擎的檔案相關的instruments,可以用如下語句查詢:


admin@localhost : performance_schema 09

:16:59

> select * from setup_instruments where name like 

'wait/io/file/innodb/%'

;

+--------------------------------------+---------+-------+
| NAME                                 | ENABLED | TIMED |
+--------------------------------------+---------+-------+
| wait/io/file/innodb/innodb_data_file | YES     | YES   |
| wait/io/file/innodb/innodb_log_file  | YES     | YES   |
| wait/io/file/innodb/innodb_temp_file | YES     | YES   |
+--------------------------------------+---------+-------+
3 rows in set ( . 00 sec)

PS:

  • 官方文件中沒有找到每一個instruments具體的說明文件,官方文件中列出如下幾個原因: 
    * instruments是服務端程式碼,所以程式碼可能經常變動 
    * instruments總數量有數百種,全部列出不現實 
    * instruments會因為你安裝的版本不同而有所不同,每一個版本所支援的instruments可以通過查詢setup_instruments表獲取

一些可能常用的場景相關的設定 :

*  metadata locks 監控需要開啟 'wait/lock/metadata/sql/mdl' instruments 才能監控,開啟這個 instruments 之後在表 performance_schema.metadata_locks 表中可以查詢到MDL鎖資訊 
profiing 探針功能即將廢棄,監控探針相關的事件資訊需要開啟語句: select * from setup_instruments where name like '%stage/sql%' and name not like '%stage/sql/Waiting%' and name not like '%stage/sql/%relay%' and name not like '%stage/sql/%binlog%' and name not like '%stage/sql/%load%'  ;返回結果集中的 instruments ,開啟這些 instruments 之後,可以在 performance_schema.events_stages_xxx 表中檢視原探針相關的事件資訊。 
* 表鎖監控需要開啟 'wait/io/table/sql/handler' instruments ,開啟這個 instruments 之後在表 performance_schema.table_handles 中會記錄了當前開啟了哪些表(執行flush tables強制關閉開啟的表時,該表中的資訊會被清空),哪些表已經被加了表鎖(某會話持有表鎖時,相關記錄行中的 OWNER_THREAD_ID OWNER_EVENT_ID 列值會記錄相關的 thread id event id ),表鎖被哪個會話持有(釋放表鎖時,相關記錄行中的 OWNER_THREAD_ID OWNER_EVENT_ID 列值會被清零) 
* 查詢語句 top number 監控,需要開啟 'statement/sql/select' instruments ,然後開啟 events_statements_xxx 表,通過查詢 performance_schema.events_statements_xxx 表的 SQL_TEXT 欄位可以看到原始的 SQL 語句,查詢 TIMER_WAIT 欄位可以知道總的響應時間, LOCK_TIME 欄位可以知道加鎖時間(注意時間單位是皮秒,需要除以 1000000000000 才是單位秒)

  • 有關setup_instruments欄位詳解

(5) setup_actors

setup_actors用於配置是否為新的前臺server執行緒(與客戶端連線相關聯的執行緒)啟用監視和歷史事件日誌記錄。預設情況下,此表的最大行數為100。可以使用系統變數 performance_schema_setup_actors_size 在server啟動之前更改此表的最大配置行數

  • 對於每個新的前臺server執行緒,perfromance_schema會匹配該表中的User,Host列進行匹配,如果匹配到某個配置行,則繼續匹配該行的ENABLED和HISTORY列值,ENABLED和HISTORY列值也會用於生成threads配置表中的行INSTRUMENTED和HISTORY列。如果使用者執行緒在建立時在該表中沒有匹配到User,Host列,則該執行緒的INSTRUMENTED和HISTORY列將設定為NO,表示不對這個執行緒進行監控,不記錄該執行緒的歷史事件資訊。

  • 對於後臺執行緒(如IO執行緒,日誌執行緒,主執行緒,purged執行緒等),沒有關聯的使用者, INSTRUMENTED和HISTORY列值預設為YES,並且後臺執行緒在建立時,不會檢視setup_actors表的配置,因為該表只能控制前臺執行緒,後臺執行緒也不具備使用者、主機屬性

setup_actors表的初始內容是匹配任何使用者和主機,因此對於所有前臺執行緒,預設情況下啟用監視和歷史事件收集功能,如下:


mysql> SELECT * FROM setup_actors;

+------+------+------+---------+---------+
| HOST | USER | ROLE | ENABLED | HISTORY |
+------+------+------+---------+---------+
| %    | %     | %    | YES     | YES    |
+------+------+------+---------+---------+

setup_actors表欄位含義如下:

  • HOST:與grant語句類似的主機名,一個具體的字串名字,或使用“%”表示“任何主機”

  • USER:一個具體的字串名稱,或使用“%”表示“任何使用者”

  • ROLE:當前未使用,MySQL 8.0中才啟用角色功能

  • ENABLED:是否啟用與HOST,USER,ROLE匹配的前臺執行緒的監控功能,有效值為:YES或NO

  • HISTORY:是否啟用與HOST, USER,ROLE匹配的前臺執行緒的歷史事件記錄功能,有效值為:YES或NO

  • PS:setup_actors表允許使用TRUNCATE TABLE語句清空表,或者DELETE語句刪除指定行

對setup_actors表的修改僅影響修改之後新建立的前臺執行緒,對於修改之前已經建立的前臺執行緒沒有影響,如果要修改已經建立的前臺執行緒的監控和歷史事件記錄功能,可以修改threads錶行的INSTRUMENTED和HISTORY列值:

當一個前臺執行緒初始化連線mysql server時,performance_schema會對錶setup_actors執行查詢,在表中查詢每個配置行,首先嚐試使用USER和HOST列(ROLE未使用)依次找出匹配的配置行,然後再找出最佳匹配行並讀取匹配行的ENABLED和HISTORY列值,用於填充threads表中的ENABLED和HISTORY列值。

  • 示例,假如setup_actors表中有如下HOST和USER值: 
    * USER ='literal' and HOST ='literal' 
    * USER ='literal' and HOST ='%' 
    * USER ='%' and HOST ='literal' 
    * USER ='%' and HOST ='%'

  • 匹配順序很重要,因為不同的匹配行可能具有不同的USER和HOST值(mysql中對於使用者帳號是使用user@host進行區分的),根據匹配行的ENABLED和HISTORY列值來決定對每個HOST,USER或ACCOUNT(USER和HOST組合,如:user@host)對應的執行緒在threads表中生成對應的匹配行的ENABLED和HISTORY列值 ,以便決定是否啟用相應的instruments和歷史事件記錄,類似如下: 
    * 當在setup_actors表中的最佳匹配行的ENABLED = YES時,threads表中對應執行緒的配置行中INSTRUMENTED列值將變為YES,HISTORY 列同理 
    * 當在setup_actors表中的最佳匹配行的ENABLED = NO時,threads表中對應執行緒的配置行中INSTRUMENTED列值將變為NO,HISTORY 列同理 
    * 當在setup_actors表中找不到匹配時,threads表中對應執行緒的配置行中INSTRUMENTED和HISTORY值值將變為NO 
    * setup_actors表配置行中的ENABLED和HISTORY列值可以相互獨立設定為YES或NO,互不影響,一個是是否啟用執行緒對應的instruments,一個是是否啟用執行緒相關的歷史事件記錄的consumers

  • 預設情況下,所有新的前臺執行緒啟用instruments和歷史事件收集,因為setup_actors表中的預設值是host='%',user='%',ENABLED='YES',HISTORY='YES'的。如果要執行更精細的匹配(例如僅對某些前臺執行緒進行監視),那就必須要對該表中的預設值進行修改,如下:



# 首先使用UPDATE語句把預設配置行禁用


UPDATE setup_actors SET ENABLED = 'NO' , HISTORY = 'NO' WHERE HOST = '%' AND USER = '%' ;
# 插入使用者joe@'localhost'對應ENABLED和HISTORY都為YES的配置行
INSERT INTO setup_actors (HOST, USER , ROLE ,ENABLED,HISTORY) VALUES ( 'localhost' , 'joe' , '%' , 'YES' , 'YES' );
# 插入使用者joe@'hosta.example.com'對應ENABLED=YES、HISTORY=NO的配置行
INSERT INTO setup_actors (HOST, USER , ROLE ,ENABLED,HISTORY) VALUES ( 'hosta.example.com' , 'joe' , '%' , 'YES' , 'NO' );
# 插入使用者sam@'%'對應ENABLED=NO、HISTORY=YES的配置行
INSERT INTO setup_actors (HOST, USER , ROLE ,ENABLED,HISTORY) VALUES ( '%' , 'sam' , '%' , 'NO' , 'YES' );
# 此時,threads表中對應使用者的前臺執行緒配置行中INSTRUMENTED和HISTORY列生效值如下
## 當joe從localhost連線到mysql server時,則連線符合第一個INSERT語句插入的配置行,threads表中對應配置行的INSTRUMENTED和HISTORY列值變為YES
## 當joe從hosta.example.com連線到mysql server時,則連線符合第二個INSERT語句插入的配置行,threads表中對應配置行的INSTRUMENTED列值為YES,HISTORY列值為NO
## 當joe從其他任意主機(%匹配除了localhost和hosta.example.com之外的主機)連線到mysql server時,則連線符合第三個INSERT語句插入的配置行,threads表中對應配置行的INSTRUMENTED和HISTORY列值變為NO
## 當sam從任意主機(%匹配)連線到mysql server時,則連線符合第三個INSERT語句插入的配置行,threads表中對應配置行的INSTRUMENTED列值變為NO,HISTORY列值為YES
## 除了joe和sam使用者之外,其他任何使用者從任意主機連線到mysql server時,匹配到第一個UPDATE語句更新之後的預設配置行,threads表中對應配置行的INSTRUMENTED和HISTORY列值變為NO
## 如果把UPDATE語句改成DELETE,讓未明確指定的使用者在setup_actors表中找不到任何匹配行,則threads表中對應配置行的INSTRUMENTED和HISTORY列值變為NO

對於後臺執行緒,對setup_actors表的修改不生效,如果要干預後臺執行緒預設的設定,需要查詢threads表找到相應的執行緒,然後使用UPDATE語句直接修改threads表中的INSTRUMENTED和HISTORY列值。

(6) setup_objects

setup_objects表控制 performance_schema 是否監視特定物件。預設情況下,此表的最大行數為100行。要更改錶行數大小,可以在server啟動之前修改系統變數 performance_schema_setup_objects_size 的值。

setup_objects表初始內容如下所示:


mysql> SELECT * FROM setup_objects;

+-------------+--------------------+-------------+---------+-------+
| OBJECT_TYPE | OBJECT_SCHEMA       | OBJECT_NAME | ENABLED | TIMED |
+-------------+--------------------+-------------+---------+-------+
| EVENT      | mysql               | %          | NO       | NO    |
| EVENT      | performance_schema | %          | NO       | NO    |
| EVENT      | information_schema | %          | NO       | NO    |
| EVENT      | %                   | %          | YES     | YES  |
| FUNCTION    | mysql               | %          | NO       | NO    |
| FUNCTION    | performance_schema | %          | NO       | NO    |
| FUNCTION    | information_schema | %          | NO       | NO    |
| FUNCTION    | %                   | %          | YES     | YES  |
| PROCEDURE  | mysql               | %          | NO       | NO    |
| PROCEDURE  | performance_schema | %          | NO       | NO    |
| PROCEDURE  | information_schema | %          | NO       | NO    |
| PROCEDURE  | %                   | %          | YES     | YES  |
| TABLE      | mysql               | %          | NO       | NO    |
| TABLE      | performance_schema | %          | NO       | NO    |
| TABLE      | information_schema | %          | NO       | NO    |
| TABLE      | %                   | %          | YES     | YES  |
| TRIGGER    | mysql               | %          | NO       | NO    |
| TRIGGER    | performance_schema | %          | NO       | NO    |
| TRIGGER    | information_schema | %          | NO       | NO    |
| TRIGGER    | %                   | %          | YES     | YES  |
+-------------+--------------------+-------------+---------+-------+

對setup_objects表的修改會立即影響物件監控

setup_objects 中列出的監控物件型別,在進行匹配時, performance_schema 基於 OBJECT_SCHEMA OBJECT_NAME 列依次往後匹配,如果沒有匹配的物件則不會被監視

預設配置中開啟監視的物件不包含 mysql,INFORMATION_SCHEMA和performance_schema 資料庫中的所有表(從上面的資訊中可以看到這幾個庫的enabled和timed欄位都為NO,注意:對於 INFORMATION_SCHEMA 資料庫,雖然該表中有一行配置,但是無論該表中如何設定,都不會監控該庫,在 setup_objects 表中 information_schema.% 的配置行僅作為一個預設值)

performance_schema setup_objects 表中進行匹配檢測時,會嘗試首先找到最具體(最精確)的匹配項。例如,在匹配 db1.t1 表時,它會從 setup_objects 表中先查詢 “db1” “t1” 的匹配項,然後再查詢 “db1” “%” ,然後再查詢 “%” “%” 。匹配的順序很重要,因為不同的匹配行可能具有不同的 ENABLED TIMED 列值

如果使用者對該表具有INSERT和DELETE許可權,則可以對該表中的配置行進行刪除和插入新的配置行。對於已經存在的配置行,如果使用者對該表具有UPDATE許可權,則可以修改ENABLED和TIMED列,有效值為:YES和NO

setup_objects表列含義如下:

  • OBJECT_TYPE instruments 型別,有效值為: “EVENT” (事件排程器事件)、 “FUNCTION” (儲存函式)、 “PROCEDURE” (儲存過程)、 “TABLE” (基表)、 “TRIGGER” (觸發器), TABLE 物件型別的配置會影響表 I/O 事件( wait/io/table/sql/handler instrument )和表鎖事件( wait/lock/table/sql/handler instrument )的收集

  • OBJECT_SCHEMA :某個監視型別物件涵蓋的資料庫名稱,一個字串名稱,或“%”(表示“任何資料庫”)

  • OBJECT_NAME :某個監視型別物件涵蓋的表名,一個字串名稱,或“%”(表示“任何資料庫內的物件”)

  • ENABLED :是否開啟對某個型別物件的監視功能,有效值為:YES或NO。此列可以修改

  • TIMED:是否開啟對某個型別物件的時間收集功能,有效值為:YES或NO,此列可以修改

  • PS:對於setup_objects表,允許使用TRUNCATE TABLE語句

setup_objects 配置表中預設的配置規則是不啟用對 mysql、INFORMATION_SCHEMA、performance_schema 資料庫下的物件進行監視的( ENABLED TIMED 列值全都為 NO

performance_schema setup_objects 表中進行查詢匹配時,如果發現某個 OBJECT_TYPE 列值有多行,則會嘗試著匹配更多的配置行,如下( performance_schema 按照如下順序進行檢查):

  • OBJECT_SCHEMA ='literal' and OBJECT_NAME ='literal'

  • OBJECT_SCHEMA ='literal' and OBJECT_NAME ='%'

  • OBJECT_SCHEMA ='%' and OBJECT_NAME ='%'

  • 例如,要匹配表物件 db1.t1,performance_schema setup_objects 表中先查詢 “OBJECT_SCHEMA = db1” “OBJECT_NAME = t1” 的匹配項,然後查詢 “OBJECT_SCHEMA = db1” “OBJECT_NAME =%” ,然後查詢 “OBJECT_SCHEMA = %” “OBJECT_NAME = %” 。匹配順序很重要,因為不同的匹配行中的 ENABLED TIMED 列可以有不同的值,最終會選擇一個最精確的匹配項

對於表物件相關事件, instruments 是否生效需要看 setup_objects setup_instruments 兩個表中的配置內容相結合,以確定是否啟用 instruments 以及計時器功能(例如前面說的I/O事件: wait/io/table/sql/handler instrument 和表鎖事件: wait/lock/table/sql/handler instrument ,在 setup_instruments 配置表中也有明確的配置選項):

  • 只有在Setup_instruments和setup_objects中的ENABLED列都為YES時,表的instruments才會生成事件資訊

  • 只有在Setup_instruments和setup_objects中的TIMED列都為YES時,表的instruments才會啟用計時器功能(收集時間資訊) 

  • 例如:要監視db1.t1、db1.t2、db2.%、db3.%這些表,setup_instruments和setup_objects兩個表中有如下配置項



# setup_instruments表


admin@localhost : performance_schema 03:06:01 > select * from setup_instruments where name like '%/table/%' ;
+-----------------------------+---------+-------+
| NAME                        | ENABLED | TIMED |
+-----------------------------+---------+-------+
| wait/io/table/sql/handler   | YES     | YES   |
| wait/lock/table/sql/handler | YES     | YES   |
+-----------------------------+---------+-------+
2 rows in set ( . 00 sec)
# setup_objects表
+-------------+---------------+-------------+---------+-------+
| OBJECT_TYPE | OBJECT_SCHEMA | OBJECT_NAME | ENABLED | TIMED |
+-------------+---------------+-------------+---------+-------+
| TABLE       | db1           | t1          | YES     | YES   |
| TABLE       | db1           | t2          | NO       | NO    |
| TABLE       | db2           | %           | YES     | YES   |
| TABLE       | db3           | %           | NO       | NO    |
| TABLE       | %             | %           | YES     | YES   |
+-------------+---------------+-------------+---------+-------+
# 以上兩個表中的配置項綜合之後,只有db1.t1、db2.%、%.%的表物件的instruments會被啟用,db1.t2和db3.%不會啟用,因為這兩個物件在setup_objects配置表中ENABLED和TIMED欄位值為NO

對於儲存程式物件相關的事件,performance_schema只需要從setup_objects表中讀取配置項的ENABLED和TIMED列值。因為儲存程式物件在setup_instruments表中沒有對應的配置項

如果永續性表和臨時表名稱相同,則在setup_objects表中進行匹配時,針對這兩種型別的表的匹配規則都同時生效(不會發生一個表啟用監控,另外一個表不啟用)

(7)threads表

threads表對於每個server執行緒生成一行包含執行緒相關的資訊,例如:顯示是否啟用監視,是否啟用歷史事件記錄功能,如下:


admin@localhost : performance_schema 04:25:55> select * from threads where TYPE='FOREGROUND' limit 2\G;

*************************** 1. row ***************************
     THREAD_ID: 43
         NAME: thread/sql/compress_gtid_table
         TYPE: FOREGROUND
PROCESSLIST_ID: 1
PROCESSLIST_USER: NULL
PROCESSLIST_HOST: NULL
PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Daemon
PROCESSLIST_TIME: 27439
PROCESSLIST_STATE: Suspending
PROCESSLIST_INFO: NULL
PARENT _THREAD_ ID: 1
         ROLE: NULL
 INSTRUMENTED: YES
       HISTORY: YES
CONNECTION_TYPE: NULL
 THREAD _OS_ ID: 3652
*************************** 2. row ***************************
............
2 rows in set (0.00 sec)

當performance_schema初始化時,它根據當時存在的執行緒每個執行緒生成一行資訊記錄在threads表中。此後,每新建一個執行緒在該表中就會新增一行對應執行緒的記錄

新執行緒資訊的INSTRUMENTED和HISTORY列值由setup_actors表中的配置決定。有關setup_actors表的詳細資訊參見3.3.5. 節

當某個執行緒結束時,會從threads表中刪除對應行。對於與客戶端會話關聯的執行緒,當會話結束時會刪除threads表中與客戶端會話關聯的執行緒配置資訊行。如果客戶端自動重新連線,則也相當於斷開一次(會刪除斷開連線的配置行)再重新建立新的連線,兩次連線建立的PROCESSLIST_ID值不同。新執行緒初始INSTRUMENTED和HISTORY值可能與斷開之前的執行緒初始INSTRUMENTED和HISTORY值不同:setup_actors表在此期間可能已更改,並且如果一個執行緒在建立之後,後續再修改了setup_actors表中的INSTRUMENTED或HISTORY列值,那麼後續修改的值不會影響到threads表中已經建立好的執行緒的INSTRUMENTED或HISTORY列值

PROCESSLIST_*開頭的列提供與 INFORMATION_SCHEMA.PROCESSLIST 表或 SHOW PROCESSLIST 語句類似的資訊。但threads表中與其他兩個資訊來源有所不同:

  • 對threads表的訪問不需要互斥體,對server效能影響最小。 而使用 INFORMATION_SCHEMA.PROCESSLIST SHOW PROCESSLIST 查詢執行緒資訊的方式會損耗一定效能,因為他們需要互斥體

  • threads表為每個執行緒提供附加資訊,例如:它是前臺還是後臺執行緒,以及與執行緒相關聯的server內部資訊

  • threads 表提供有關後臺執行緒的資訊,而 INFORMATION_SCHEMA.PROCESSLIST和SHOW PROCESSLIST 不能提供

  • 可以通過threads表中的INSTRUMENTED欄位靈活地動態開關某個執行緒的監視功能、HISTORY欄位靈活地動態開關某個執行緒的歷史事件日誌記錄功能。要控制新的前臺執行緒的初始INSTRUMENTED和HISTORY列值,通過setup_actors表的HOST、 USER對某個主機、使用者進行配置。要控制已建立執行緒的採集和歷史事件記錄功能,通過threads表的INSTRUMENTED和HISTORY列進行設定

  • 對於 INFORMATION_SCHEMA.PROCESSLIST SHOW PROCESSLIST ,需要有 PROCESS 許可權,對於threads表只要有 SELECT 許可權就可以檢視所有使用者的執行緒資訊

threads表欄位含義如下:

  • THREAD_ID:執行緒的唯一識別符號(ID)

  • NAME:與server中的執行緒檢測程式碼相關聯的名稱(注意,這裡不是instruments名稱)。例如,thread/sql/one_connection對應於負責處理使用者連線的程式碼中的執行緒函式名,thread/sql/main表示server的main()函式名稱

  • TYPE:執行緒型別,有效值為: FOREGROUND、BACKGROUND 。分別表示前臺執行緒和後臺執行緒,如果是使用者建立的連線或者是複製執行緒建立的連線,則標記為前臺執行緒(如:複製IO和SQL執行緒,worker執行緒,dump執行緒等),如果是server內部建立的執行緒(不能使用者干預的執行緒),則標記為後臺執行緒,如:innodb的後臺IO執行緒等

  • PROCESSLIST_ID :對應 INFORMATION_SCHEMA.PROCESSLIST 表中的ID列。該列值與 show processlist 語句、 INFORMATION_SCHEMA.PROCESSLIST 表、connection_id()函式返回的執行緒ID值相等。另外,threads表中記錄了內部執行緒,而processlist表中沒有記錄內部執行緒,所以,對於內部執行緒,在threads表中的該欄位顯示為NULL,因此在threads表中NULL值不唯一(可能有多個後臺執行緒)

  • PROCESSLIST_USER :與前臺執行緒相關聯的使用者名稱,對於後臺執行緒為NULL。

  • PROCESSLIST_HOST :與前臺執行緒關聯的客戶端的主機名,對於後臺執行緒為 NULL 。與 INFORMATION_SCHEMA PROCESSLIST 表的 HOST 列或 SHOW PROCESSLIST 輸出的主機列不同, PROCESSLIST_HOST 列不包括 TCP/IP 連線的埠號。要從 performance_schema 中獲取埠資訊,需要查詢 socket_instances 表(關於 socket instruments wait/io/socket/sql/* 預設關閉):

  • PROCESSLIST_DB :執行緒的預設資料庫,如果沒有,則為NULL。

  • PROCESSLIST_COMMAND :對於前臺執行緒,該值代表著當前客戶端正在執行的command型別,如果是sleep則表示當前會話處於空閒狀態。有關執行緒command的詳細說明,參見連結:

    https://dev.mysql.com/doc/refman/5.7/en/thread-information.html 。對於後臺執行緒不會執行這些command,因此此列值可能為NULL

  • PROCESSLIST_TIME :當前執行緒已處於當前執行緒狀態的持續時間(秒)

  • PROCESSLIST_STATE :表示執行緒正在做什麼事情。有關 PROCESSLIST_STATE 值的說明,詳見連結:

    https://dev.mysql.com/doc/refman/5.7/en/thread-information.html 。如果列值為NULL,則該執行緒可能處於空閒狀態或者是一個後臺執行緒。大多數狀態值停留的時間非常短暫。如果一個執行緒在某個狀態下停留了非常長的時間,則表示可能有效能問題需要排查

  • PROCESSLIST_INFO :執行緒正在執行的語句,如果沒有執行任何語句,則為NULL。該語句可能是傳送到server的語句,也可能是某個其他語句執行時內部呼叫的語句。例如:如果CALL語句執行儲存程式,則在儲存程式中正在執行SELECT語句,那麼 PROCESSLIST_INFO 值將顯示SELECT語句

  • PARENT_THREAD_ID :如果這個執行緒是一個子執行緒(由另一個執行緒生成),那麼該欄位顯示其父執行緒ID

  • ROLE :暫未使用

  • INSTRUMENTED : 
    * 執行緒執行的事件是否被檢測。有效值:YES、NO 
    * 1)、對於前臺執行緒,初始INSTRUMENTED值還需要看控制前臺執行緒的setup_actors表中的INSTRUMENTED欄位值。如果在setup_actors表中找到了對應的使用者名稱和主機行,則會用該表中的INSTRUMENTED欄位生成theads表中的INSTRUMENTED欄位值,setup_actors表中的USER和HOST欄位值也會一併寫入到threads表的 PROCESSLIST_USER PROCESSLIST_HOST 列。如果某個執行緒產生一個子執行緒,則子執行緒會再次與setup_actors表進行匹配 
    * 2)、對於後臺執行緒,INSTRUMENTED預設為YES。 初始值無需檢視setup_actors表,因為該表不控制後臺執行緒,因為後臺執行緒沒有關聯的使用者 
    * 3)、對於任何執行緒,其INSTRUMENTED值可以線上程的生命週期內更改 
    * 要監視執行緒產生的事件,如下條件需滿足: 
    * 1)、 setup_consumers 表中的 thread_instrumentation consumers 必須為YES 
    * 2)、 threads.INSTRUMENTED 列必須為YES 
    * 3)、 setup_instruments 表中執行緒相關的instruments配置行的ENABLED列必須為YES 
    * 4)、如果是前臺執行緒,那麼setup_actors中對應主機和使用者的配置行中的INSTRUMENTED列必須為YES

  • HISTORY: 
    * 是否記錄執行緒的歷史事件。有效值:YES、NO 
    * 1)、對於前臺執行緒,初始HISTORY值還需要看控制前臺執行緒的setup_actors表中的HISTORY欄位值。如果在setup_actors表中找到了對應的使用者名稱和主機行,則會用該表中的HISTORY欄位生成theads表中的HISTORY欄位值,setup_actors表中的USER和HOST欄位值也會一併寫入到threads表的PROCESSLIST_USER和PROCESSLIST_HOST列。如果某個執行緒產生一個子執行緒,則子執行緒會再次與setup_actors表進行匹配 
    * 2)、對於後臺執行緒,HISTORY預設為YES。初始值無需檢視setup_actors表,因為該表不控制後臺執行緒,因為後臺執行緒沒有關聯的使用者 
    * 3)、對於任何執行緒,其HISTORY值可以線上程的生命週期內更改 
    * 要記錄執行緒產生的歷史事件,如下條件需滿足: 
    * 1)、setup_consumers表中相關聯的consumers配置必須啟用,如:要記錄執行緒的等待事件歷史記錄,需要啟用events_waits_history和events_waits_history_long consumers 
    * 2)、threads.HISTORY列必須為YES 
    * 3)、setup_instruments表中相關聯的instruments配置必須啟用 
    * 4)、如果是前臺執行緒,那麼setup_actors中對應主機和使用者的配置行中的HISTORY列必須為YES

  • CONNECTION_TYPE:用於建立連線的協議,如果是後臺執行緒則為NULL。有效值為:TCP/IP(不使用SSL建立的TCP/IP連線)、SSL/TLS(與SSL建立的TCP/IP連線)、Socket(Unix套接字檔案連線)、Named Pipe(Windows命名管道連線)、Shared Memory(Windows共享記憶體連線)

  • THREAD_OS_ID: 
    * 由作業系統層定義的執行緒或任務識別符號(ID): 
    * 1)、當一個MySQL執行緒與作業系統中與某個執行緒關聯時,那麼THREAD_OS_ID欄位可以檢視到與這個mysql執行緒相關聯的作業系統執行緒ID 
    * 2)、當一個MySQL執行緒與作業系統執行緒不關聯時,THREAD_OS_ID列值為NULL。例如:使用者使用執行緒池外掛時 
    * 對於Windows,THREAD_OS_ID對應於Process Explorer中可見的執行緒ID 
    * 對於Linux,THREAD_OS_ID對應於gettid()函式獲取的值。例如:使用perf或ps -L命令或proc檔案系統(/proc/[pid]/task/[tid])可以檢視此值。

  • PS:threads表不允許使用TRUNCATE TABLE語句

關於執行緒類物件,前臺執行緒與後臺執行緒還有少許差別

  • 對於前臺執行緒(由客戶端連線產生的連線,可以是使用者發起的連線,也可以是不同server之間發起的連線),當使用者或者其他server與某個server建立了一個連線之後(連線方式可能是socket或者TCP/IP),在threads表中就會記錄一條這個執行緒的配置資訊行,此時,threads表中該執行緒的配置行中的INSTRUMENTED和HISTORY列值的預設值是YES還是NO,還需要看與執行緒相關聯的使用者帳戶是否匹配setup_actors表中的配置行(檢視某使用者在setup_actors表中配置行的ENABLED和HISTORY列配置為YES還是NO,threads表中與setup_actors表關聯使用者帳號的執行緒配置行中的ENABLED和HISTORY列值以setup_actors表中的值為準)

  • 對於後臺執行緒,不可能存在關聯的使用者,所以threads表中的 INSTRUMENTED和HISTORY線上程建立時的初始配置列值預設值為YES,不需要檢視setup_actors表

關閉與開啟所有後臺執行緒的監控採集功能



# 關閉所有後臺執行緒的事件採集


root@localhost : performance_schema 05:46:17> update threads set INSTRUMENTED= 'NO' where TYPE= 'BACKGROUND' ;
Query OK, 40 rows affected (0.00 sec)
Rows matched: 40  Changed: 40  Warnings: 0
# 開啟所有後臺執行緒的事件採集
root@localhost : performance_schema 05:47:08> update threads set INSTRUMENTED= 'YES' where TYPE= 'BACKGROUND' ;
Query OK, 40 rows affected (0.00 sec)
Rows matched: 40  Changed: 40  Warnings: 0

關閉與開啟除了當前連線之外的所有執行緒的事件採集(不包括後臺執行緒)



# 關閉除了當前連線之外的所有前臺執行緒的事件採集


root@localhost : performance_schema 05 : 47 : 44 > update threads set INSTRUMENTED= 'NO' where PROCESSLIST_ID!=connection_id();
Query OK, 2 rows affected ( 0.00 sec )
Rows matched: 2  Changed: 2  Warnings: 0
# 開啟除了當前連線之外的所有前臺執行緒的事件採集
root@localhost : performance_schema 05:48:32> update threads set INSTRUMENTED
= 'YES' where PROCESSLIST_ID!=connection_id();
Query OK, 2 rows affected ( 0.00 sec )
Rows matched: 2  Changed: 2  Warnings: 0
# 當然,如果要連後臺執行緒一起操作,請帶上條件PROCESSLIST_ID is NULL
update ... where PROCESSLIST_ID!
=connection_id() or PROCESSLIST_ID is NULL;




本篇內容到這裡就接近尾聲了,如果閱讀了本章內容之後,感覺對 performance_schema 仍然比較迷糊,那麼建議按照如下步驟動動手、看一看: 

  • 使用命令列命令 mysqld --verbose --help |grep performance-schema |grep -v '--' |sed '1d' |sed '/[0-9]+/d'; 檢視完整的啟動選項列表 

  • 登入到資料庫中使用 show variables like '%performance_schema%';語句檢視完整的system variables列表 

  • 登入到資料庫中使用 use performance_schema;語句切換到schema下,然後使用show tables;語句檢視一下完整的table列表,並手工執行show create table tb_xxx;檢視錶結構,select * from xxx;檢視錶中的內容

performance_schema配置部分為整個performance_schema的難點,為了後續更好地學習performance_schema,建議初學者本章內容多讀兩遍。


等待事件表


通常,我們在碰到效能瓶頸時,如果其他的方法難以找出效能瓶頸的時候(例如:硬體負載不高、SQL優化和庫表結構優化都難以奏效的時候),我們常常需要藉助於等待事件來進行分析,找出在MySQL Server內部,到底資料庫響應慢是慢在哪裡。

等待事件記錄表包含三張表,這些表記錄了當前與最近在MySQL例項中發生了哪些等待事件,時間消耗是多少。

  • events_waits_current表:記錄當前正在執行的等待事件的,每個執行緒只記錄1行記錄

  • events_waits_history表:記錄已經執行完的最近的等待事件歷史,預設每個執行緒只記錄10行記錄

  • events_waits_history_long表:記錄已經執行完的最近的等待事件歷史,預設所有執行緒的總記錄行數為10000行

要注意:等待事件相關配置中,setup_instruments表中絕大部分的等待事件instruments都沒有開啟(IO相關的等待事件instruments預設大部分已開啟),setup_consumers表中waits相關的consumers配置預設沒有開啟


events_waits_current 表


events_waits_current表包含當前的等待事件資訊,每個執行緒只顯示一行最近監視的等待事件的當前狀態

在所有包含等待事件行的表中,events_waits_current表是最基礎的資料來源。其他包含等待事件資料表在邏輯上是來源於events_waits_current表中的當前事件資訊(彙總表除外)。例如,events_waits_history和events_waits_history_long表中的資料是events_waits_current表資料的一個小集合彙總(具體存放多少行資料集合有各自的變數控制)

表記錄內容示例(這是一個執行select sleep(100);語句的執行緒等待事件資訊)


root@localhost : performance

_schema 12:15:03> select * from events_

waits

_current where EVENT_

NAME='wait/synch/cond/sql/Item

_func_

sleep::cond'\G;

*************************** 1. row ***************************
       THREAD_ID: 46
       EVENT_ID: 140
   END_EVENT_ID: NULL
     EVENT_NAME: wait/synch/cond/sql/Item_func_sleep::cond
         SOURCE: item_func.cc:5261
     TIMER_START: 14128809267002592
       TIMER_END: 14132636159944419
     TIMER_WAIT: 3826892941827
           SPINS: NULL
   OBJECT_SCHEMA: NULL
     OBJECT_NAME: NULL
     INDEX_NAME: NULL
     OBJECT_TYPE: NULL
OBJECT _INSTANCE_ BEGIN: 140568905519072
NESTING _EVENT_ ID: 116
NESTING _EVENT_ TYPE: STATEMENT
       OPERATION: timed_wait
 NUMBER _OF_ BYTES: NULL
           FLAGS: NULL
1 row in set (0.00 sec)


上面的輸出結果中,TIMER_WAIT欄位即表示該事件的時間開銷,單位是皮秒,在實際的應用場景中,我們可以利用該欄位資訊進行倒序排序,以便找出時間開銷最大的等待事件。

events_waits_current 表完整的欄位含義如下:

THREAD_ID,EVENT_ID: 與事件關聯的執行緒ID和當前事件ID。 THREAD_ID EVENT_ID 值構成了該事件資訊行的唯一標識(不會有重複的 THREAD_ID+EVENT_ID 值)

END_EVENT_ID :當一個事件正在執行時該列值為NULL,當一個事件執行結束時把該事件的ID更新到該列

EVENT_NAME: 產生事件的 instruments 名稱。該名稱來自 setup_instruments 表的 NAME 欄位值

SOURCE :產生該事件的instruments所在的原始檔名稱以及檢測到該事件發生點的程式碼行號。您可以檢視原始碼來確定涉及的程式碼。例如,如果互斥鎖、鎖被阻塞,您可以檢查發生這種情況的上下文環境

TIMER_START,TIMER_END,TIMER_WAIT :事件的時間資訊。單位皮秒(萬億分之一秒)。 TIMER_START和TIMER_END值表示事件開始和結束時間。 TIMER_WAIT是事件經過時間(即事件執行了多長時間) 

  • 如果事件未執行完成,則TIMER_END為當前計時器時間值(當前時間),TIMER_WAIT為目前為止所經過的時間(TIMER_END - TIMER_START) 

  • 如果採集該事件的instruments配置項TIMED = NO,則不會收集事件的時間資訊,TIMER_START,TIMER_END和TIMER_WAIT在這種情況下均記錄為NULL 

SPINS:對於互斥量和自旋次數。如果該列值為NULL,則表示程式碼中沒有使用自旋或者說自旋沒有被監控起來 

OBJECT_SCHEMA,OBJECT_NAME,OBJECT_TYPE,OBJECT_INSTANCE_BEGIN: 這些列標識了一個正在被執行的物件,所以這些列記錄的資訊含義需要看物件是什麼型別, 下面按照不同物件型別分別對這些列的含義進行說明: 

* 對於同步物件(cond,mutex,rwlock): 

* 1)、OBJECT_SCHEMA,OBJECT_NAME和OBJECT_TYPE列值都為NULL

* 2)、 OBJECT_INSTANCE_BEGIN 列是記憶體中同步物件的地址。 OBJECT_INSTANCE_BEGIN 除了不同的值標記不同的物件之外,其值本身沒有意義。但 OBJECT_INSTANCE_BEGIN 值可用於除錯。例如,它可以與 GROUP BY OBJECT_INSTANCE_BEGIN 子句一起使用來檢視 1,000 個互斥體(例如:保護 1,000 個頁或資料塊)上的負載是否是均勻分佈還是發生了一些瓶頸。如果在日誌檔案或其他除錯、效能工具中看到與該語句檢視的結果中有相同的物件地址,那麼,在你分析效能問題時,可以把這個語句檢視到的資訊與其他工具檢視到的資訊關聯起來。 

* 對於檔案I/O物件:  

* 1)、 OBJECT_SCHEMA 列值為 NULL 
* 2)、 OBJECT_NAME 列是檔名 
* 3)、 OBJECT_TYPE 列為 FILE 
* 4)、 OBJECT_INSTANCE_BEGIN 列是記憶體中的地址,解釋同上 

* 對於套接字物件:  

* 1)、OBJECT_NAME列是套接字的IP:PORT值 

* 2)、OBJECT_INSTANCE_BEGIN列是記憶體中的地址,解釋同上 

* 對於表I/O物件: 

* 1)、OBJECT_SCHEMA列是包含該表的庫名稱 

* 2)、OBJECT_NAME列是表名 

* 3)、OBJECT_TYPE列值對於基表或者TEMPORARY TABLE臨時表,該值是table,注意:對於在join查詢中select_type為DERIVED,subquery等的表可能不記錄事件資訊也不進行統計 

* 4)、OBJECT_INSTANCE_BEGIN列是記憶體中的地址,解釋同上

INDEX_NAME: 表示使用的索引的名稱。 PRIMARY 表示使用到了主鍵。  NULL 表示沒有使用索引

NESTING_EVENT_ID :表示該行資訊中的 EVENT_ID 事件是巢狀在哪個事件中,即父事件的 EVENT_ID

NESTING_EVENT_TYPE :表示該行資訊中的 EVENT_ID 事件巢狀的事件型別。有效值有: TRANSACTION,STATEMENT,STAGE WAIT ,即父事件的事件型別,如果為 TRANSACTION 則需要到事務事件表中找對應 NESTING_EVENT_ID 值的事件,其他型別同理

OPERATION :執行的操作型別,如: lock、read、write、timed_wait

NUMBER_OF_BYTES :操作讀取或寫入的位元組數或行數。對於檔案IO等待,該列值表示位元組數;對於表 I/O 等待( wait/io/table/sql/handler instruments 的事件),該列值表示行數。如果值大於1,則表示該事件對應一個批量 I/O 操作。 以下分別對單個表 IO 和批量表 IO 的區別進行描述:  

  • MySQL的join查詢使用巢狀迴圈實現。performance_schema instruments的作用是在join查詢中提供對每個表的掃描行數和執行時間進行統計。示例:join查詢語句:SELECT … FROM t1 JOIN t2 ON … JOIN t3 ON …,假設join順序是t1,t2,t3 

  • 在join查詢中,一個表在查詢時與其他表展開聯結查詢之後,該表的掃描行數可能增加也可能減少,例如:如果t3表扇出大於1,則大多數row fetch操作都是針對t3表,假如join查詢從t1表訪問10行記錄,然後使用t1表驅動查詢t2表,t1表的每一行都會掃描t2表的20行記錄,然後使用t2表驅動查詢t3表,t2表的每一行都會掃描t3表的30行記錄,那麼,在使用單行輸出時,instruments統計操作的事件資訊總行數為:10 +(10 * 20)+(10 * 20 * 30)= 6210 

  • 通過對錶中行掃描時的instruments統計操作進行聚合(即,每個t1和t2的掃描行數在instruments統計中可以算作一個批量組合),這樣就可以減少instruments統計操作的數量。通過批量I/O輸出方式,performance_schema每次對最內層表t3的掃描減少為一個事件統計資訊而不是每一行掃描都生成一個事件資訊,此時對於instruments統計操作的事件行數量減少到:10 +(10 * 20)+(10 * 20)= 410,這樣在該join查詢中對於performance_schema中的行統計操作就減少了93%,批量輸出策略通過減少輸出行數量來顯著降低表I/O的performance_schema統計開銷。但是相對於每行資料都單獨執行統計操作,會損失對時間統計的準確度。在join查詢中,批量I/O統計的時間包括用於連線緩衝、聚合和返回行到客戶端的操作所花費的時間(即就是整個join語句的執行時間)

FLAGS: 留作將來使用

PS:events_waits_current 表允許使用 TRUNCATE TABLE 語句

events_waits_history 表

events_waits_history表包含每個執行緒最近的N個等待事件。 在server啟動時,N的值會自動調整。 如果要顯式設定這個N大小,可以在server啟動之前調整系統引數performance_schema_events_waits_history_size的值。 等待事件需要執行結束時才被新增到events_waits_history表中(沒有結束時儲存在events_waits_current表)。當新增新事件到events_waits_history表時,如果該表已滿,則會丟棄每個執行緒較舊的事件

events_waits_history events_waits_current 表定義相同

PS:允許執行TRUNCATE TABLE語句


events_waits_history_long 表


events_waits_history_long表包含最近的N個等待事件(所有執行緒的事件)。在 server 啟動時,N的值會自動調整。 如果要顯式設定這個N大小,可以在 server 啟動之前調整系統引數

performance_schema_events_waits_history_long_size 的值。等待事件需要執行結束時才會被新增到 events_waits_history_long 表中(沒有結束時儲存在 events_waits_current 表),當新增新事件到 events_waits_history_long 表時,如果該表已滿,則會丟棄該表中較舊的事件。

events_waits_history_long events_waits_current 表結構相同

PS:允許使用TRUNCATE TABLE語句


階段事件表


階段事件記錄表與等待事件記錄表一樣,也有三張表,這些表記錄了當前與最近在MySQL例項中發生了哪些階段事件,時間消耗是多少。階段指的是語句執行過程中的步驟,例如:parsing 、opening tables、filesort操作等。

在以往我們檢視語句執行的階段狀態,常常使用SHOW PROCESSLIST語句或查詢INFORMATION_SCHEMA.PROCESSLIST表來獲得,但processlist方式能夠查詢到的資訊比較有限且轉瞬即逝,我們常常需要結合profiling功能來進一步統計分析語句執行的各個階段的開銷等,現在,我們不需要這麼麻煩,直接使用performance_schema的階段事件就既可以查詢到所有的語句執行階段,也可以查詢到各個階段對應的開銷,因為是記錄在表中,所以更可以使用SQL語句對這些資料進行排序、統計等操作

要注意:階段事件相關配置中, setup_instruments 表中 stage/ 開頭的絕大多數 instruments 配置預設沒有開啟(少數 stage /開頭的 instruments 除外,如 DDL 語句執行過程的 stage/innodb/alter* 開頭的 instruments 預設開啟的), setup_consumers表 stages 相關的 consumers 配置預設沒有開啟


events_stages_current 表


events_stages_current表包含當前階段事件的監控資訊,每個執行緒一行記錄顯示執行緒正在執行的stage事件的狀態

在包含stage事件記錄的表中,events_stages_current是基準表,包含stage事件記錄的其他表(如:events_stages_history和events_stages_history_long表)的資料在邏輯上都來自events_stages_current表(彙總表除外) 

表記錄內容示例(以下仍然是一個執行select sleep(100);語句的執行緒,但這裡是階段事件資訊)


root@localhost : performance

_schema 12:24:40> select * from events_

stages

_current where EVENT_

NAME='stage/sql/User sleep'\G;

*************************** 1. row ***************************
   THREAD_ID: 46
     EVENT_ID: 280
 END _EVENT_ ID: NULL
   EVENT_NAME: stage/sql/User sleep
       SOURCE: item_func.cc:6056
 TIMER_START: 14645080545642000
   TIMER_END: 14698320697396000
   TIMER_WAIT: 53240151754000
WORK_COMPLETED: NULL
WORK_ESTIMATED: NULL
NESTING _EVENT_ ID: 266
NESTING _EVENT_ TYPE: STATEMENT
1 row in set (0.00 sec)

以上的輸出結果與語句的等待事件形式類似,這裡不再贅述, events_stages_current 表完整的欄位含義如下

THREAD_ID,EVENT_ID :與事件關聯的執行緒ID和當前事件ID,可以使用 THREAD_ID EVENT_ID 列值來唯一標識該行,這兩行的值作為組合條件時不會出現相同的資料行

END_EVENT_ID :當一個事件開始執行時,對應行記錄的該列值被設定為 NULL ,當一個事件執行結束時,對應的行記錄的該列值被更新為該事件的ID

EVENT_NAME :產生事件的 instruments 的名稱。該列值來自 setup_instruments 表的 NAME 值。 instruments 名稱可能具有多個部分並形成層次結構,如: "stage/sql/Slave has read all relay log; waiting for more updates", 其中 stage 是頂級名稱,sql是二級名稱, Slave has read all relay log; waiting for more updates 是第三級名稱。詳見連結:

https://dev.mysql.com/doc/refman/5.7/en/performance-schema-instrument-naming.html

SOURCE :原始檔的名稱及其用於檢測該事件的程式碼位於原始檔中的行號

TIMER_START,TIMER_END,TIMER_WAIT :事件的時間資訊。這些值的單位是皮秒(萬億分之一秒)。  TIMER_START TIMER_END 值表示事件的開始時間和結束時間。 TIMER_WAIT 是事件執行消耗的時間(持續時間) 

  • 如果事件未執行完成,則TIMER_END為當前時間,TIMER_WAIT為當前為止所經過的時間(TIMER_END - TIMER_START)

  • 如果instruments配置表setup_instruments中對應的instruments 的TIMED欄位被設定為 NO,則該instruments禁用時間收集功能,那麼事件採集的資訊記錄中,TIMER_START,TIMER_END和TIMER_WAIT欄位值均為NULL

WORK_COMPLETED,WORK_ESTIMATED:這些列提供了階段事件進度資訊 

  • 表中的WORK_COMPLETED和WORK_ESTIMATED兩列,它們共同協作顯示每一行的進度顯示: 

* 1)、WORK_COMPLETED:顯示階段事件已完成的工作單元數 

* 2)、WORK_ESTIMATED:顯示預計階段事件將要完成的工作單元數 

  • 如果instruments沒有提供進度相關的功能,則該instruments執行事件採集時就不會有進度資訊顯示,WORK_COMPLETED和WORK_ESTIMATED列都會顯示為NULL。如果進度資訊可用,則進度資訊如何顯示取決於instruments的執行情況。performance_schema表提供了一個儲存進度資料的容器,但不會假設你會定義何種度量單位來使用這些進度資料: 

* 1)、“工作單元”是在執行過程中隨時間增加而增加的整數度量,例如執行過程中的位元組數、行數、檔案數或表數。對於特定instruments的“工作單元”的定義留給提供資料的instruments程式碼 

* 2)、WORK_COMPLETED值根據檢測的程式碼不同,可以一次增加一個或多個單元 

* 3)、WORK_ESTIMATED值根據檢測程式碼,可能在階段事件執行過程中發生變化 

  • 階段事件進度指示器的表現行為有以下幾種情況: 

1)、instruments不支援進度:沒有可用進度資料, WORK_COMPLETED和WORK_ESTIMATED列都顯示為NULL 

* 2) 、instruments支援進度但對應的工作負載總工作量不可預估(無限進度):只有WORK_COMPLETED列有意義(因為他顯示正在執行的進度顯示),WORK_ESTIMATED列此時無效,顯示為0,因為沒有可預估的總進度資料。通過查詢events_stages_current表來監視會話,監控應用程式到目前為止執行了多少工作,但無法報告對應的工作是否接近完成 

* 3)、instruments支援進度,總工作量可預估(有限進度):WORK_COMPLETED和WORK_ESTIMATED列值有效。這種型別的進度顯示可用於online DDL期間的copy表階段監視。通過查詢events_stages_current表,可監控應用程式當前已經完成了多少工作,並且可以通過WORK_COMPLETED / WORK_ESTIMATED計算的比率來預估某個階段總體完成百分比

NESTING_EVENT_ID :事件的巢狀事件 EVENT_ID 值(父事件ID)

NESTING_EVENT_TYPE :巢狀事件型別。有效值為: TRANSACTION,STATEMENT,STAGE,WAIT。 階段事件的巢狀事件通常是 statement

對於events_stages_current表允許使用TRUNCATE TABLE語句來進行清理

PS:stage事件擁有一個進度展示功能,我們可以利用該進度展示功能來了解一些長時間執行的SQL的進度百分比,例如:對於需要使用COPY方式執行的online ddl,那麼需要copy的資料量是一定的,可以明確的,so..這就可以為"stage/sql/copy to tmp table stage" instruments提供一個有結束邊界參照的進度資料資訊,這個instruments所使用的工作單元就是需要複製的資料行數,此時WORK_COMPLETED和WORK_ESTIMATED列值都是有效的可用的,兩者的計算比例就表示當前copy表完成copy的行資料百分比。

  • 要檢視copy表階段事件的正在執行的進度監視功能,需要開啟相關的instruments和consumers,然後檢視events_stages_current表,如下:



# 配置相關instruments和consumers


UPDATE setup_instruments SET ENABLED= 'YES' WHERE NAME = 'stage/sql/copy to tmp table' ;
UPDATE setup_consumers SET ENABLED= 'YES' WHERE NAME LIKE 'events_stages_%' ;
# 然後在執行ALTER TABLE語句期間,檢視events_stages_current表



events_stages_history 表


events_stages_history表包含每個執行緒最新的N個階段事件。 在server啟動時,N的值會自動調整。 如果要顯式設定N值大小,可以在server啟動之前設定系統變數performance_schema_events_stages_history_size的值。stages事件在執行結束時才新增到events_stages_history表中。 當新增新事件到events_stages_history表時,如果events_stages_history表已滿,則會丟棄對應執行緒較舊的事件events_stages_history與events_stages_current表結構相同

PS:允許使用TRUNCATE TABLE語句


events_stages_history_long 表


events_stages_history_long表包含最近的N個階段事件。 在server啟動時,N的值會自動調整。 如果要顯式設定N值大小,可以在server啟動之前設定系統變數performance_schema_events_stages_history_long_size的值。stages事件執行結束時才會新增到events_stages_history_long表中,當新增新事件到events_stages_history_long表時,如果events_stages_history_long表已滿,則會丟棄該表中較舊的事件events_stages_history_long與events_stages_current表結構相同 

PS:允許使用TRUNCATE TABLE語句


語句事件表


語句事件記錄表與等待事件記錄表一樣,也有三張表,這些表記錄了當前與最近在MySQL例項中發生了哪些語句事件,時間消耗是多少。記錄了各種各樣的語句執行產生的語句事件資訊。 

要注意:語句事件相關配置中, setup_instruments 表中 statement/* 開頭的所有 instruments 配置預設開啟, setup_consumers 表中 statements 相關的 consumers 配置預設開啟了 events_statements_current、events_statements_history、 statements_digest (對應 events_statements_summary_by_digest 表,詳見後續章節)但沒有開啟 events_statements_history_long。


events_statements_current 表


events_statements_current表包含當前語句事件,每個執行緒只顯示一行最近被監視語句事件的當前狀態。

在包含語句事件行的表中,events_statements_current當前事件表是基礎表。其他包含語句事件表中的資料在邏輯上來源於當前事件表(彙總表除外)。例如:events_statements_history和events_statements_history_long表是最近的語句事件歷史的集合,events_statements_history表中每個執行緒預設保留10行事件歷史資訊,events_statements_history_long表中預設所有執行緒保留10000行事件歷史資訊

表記錄內容示例(以下資訊仍然來自select sleep(100);語句的語句事件資訊)


root@localhost : performance_schema 

12

:

36

:

35

> select * from events_statements_current where SQL_TEXT=

'select sleep(100)'

\G;

*************************** 1. row ***************************
         THREAD_ID: 46
         EVENT_ID: 334
     END_EVENT_ID: NULL
       EVENT_NAME: statement/sql/select
           SOURCE: socket_connection.cc: 101
       TIMER_START: 15354770719802000
         TIMER_END: 15396587017809000
       TIMER_WAIT: 41816298007000
         LOCK_TIME:
         SQL_TEXT: select sleep( 100 )
           DIGEST: NULL
       DIGEST_TEXT: NULL
   CURRENT_SCHEMA: NULL
       OBJECT_TYPE: NULL
     OBJECT_SCHEMA: NULL
       OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: NULL
       MYSQL_ERRNO:
 RETURNED_SQLSTATE: NULL
     MESSAGE_TEXT: NULL
           ERRORS:
         WARNINGS:
     ROWS_AFFECTED:
         ROWS_SENT:
     ROWS_EXAMINED:
CREATED_TMP_DISK_TABLES:
CREATED_TMP_TABLES:
 SELECT_FULL_JOIN:
SELECT_FULL_RANGE_JOIN:
     SELECT_RANGE:
SELECT_RANGE_CHECK:
       SELECT_SCAN:
 SORT_MERGE_PASSES:
       SORT_RANGE:
         SORT_ROWS:
         SORT_SCAN:
     NO_INDEX_USED:
NO_GOOD_INDEX_USED:
 NESTING_EVENT_ID: NULL
NESTING_EVENT_TYPE: NULL
NESTING_EVENT_LEVEL:
1 row in set ( 0.00 sec)


以上的輸出結果與語句的等待事件形式類似,這裡不再贅述, events_statements_current 表完整的欄位含義如下:

THREAD_ID,EVENT_ID:與事件關聯的執行緒號和事件啟動時的事件編號,可以使用THREAD_ID和EVENT_ID列值來唯一標識該行,這兩行的值作為組合條件時不會出現相同的資料行

END_EVENT_ID:當一個事件開始執行時,對應行記錄的該列值被設定為NULL,當一個事件執行結束時,對應的行記錄的該列值被更新為該事件的ID

EVENT_NAME:產生事件的監視儀器的名稱。該列值來自setup_instruments表的NAME值。對於SQL語句,EVENT_NAME值最初的instruments是statement/com/Query,直到語句被解析之後,會更改為更合適的具體instruments名稱,如:statement/sql/insert

SOURCE:原始檔的名稱及其用於檢測該事件的程式碼位於原始檔中的行號,您可以檢查原始碼來確定涉及的程式碼

TIMER_START,TIMER_END,TIMER_WAIT:事件的時間資訊。這些值的單位是皮秒(萬億分之一秒)。 TIMER_START和TIMER_END值表示事件的開始時間和結束時間。TIMER_WAIT是事件執行消耗的時間(持續時間) 

  • 如果事件未執行完成,則TIMER_END為當前時間,TIMER_WAIT為當前為止所經過的時間(TIMER_END - TIMER_START)。 

  • 如果監視儀器配置表setup_instruments中對應的監視器TIMED欄位被設定為 NO,則不會收集該監視器的時間資訊,那麼對於該事件採集的資訊記錄中,TIMER_START,TIMER_END和TIMER_WAIT欄位值均為NULL 

LOCK_TIME:等待表鎖的時間。該值以微秒進行計算,但最終轉換為皮秒顯示,以便更容易與其他performance_schema中的計時器進行比較

SQL_TEXT:SQL 語句的文字。如果該行事件是與 SQL 語句無關的 command 事件,則該列值為 NULL 。預設情況下,語句最大顯示長度為 1024 位元組。如果要修改,則在 server 啟動之前設定系統變數 performance_schema_max_sql_text_length 的值 

DIGEST :語句摘要的 MD5 hash 值,為32位十六進位制字串,如果在 setup_consumers表中statement_digest 配置行沒有開啟,則語句事件中該列值為 NULL  

DIGEST_TEXT:標準化轉換過的語句摘要文字,如果setup_consumers表中statements_digest配置行沒有開啟,則語句事件中該列值為NULL。performance_schema_max_digest_length系統變數決定著在存入該表時的最大摘要語句文字的位元組長度(預設為1024位元組),要注意:用於計算摘要語句文字的原始語句文字位元組長度由系統變數max_digest_length控制,而存入表中的位元組長度由系統變數performance_schema_max_digest_length控制,所以,如果performance_schema_max_digest_length小於max_digest_length時,計算出的摘要語句文字如果大於了performance_schema_max_digest_length定義的長度會被截斷

CURRENT_SCHEMA :語句使用的預設資料庫(使用 use db_name 語句即可指定預設資料庫),如果沒有則為 NULL  

OBJECT_SCHEMA,OBJECT_NAME,OBJECT_TYPE :對於巢狀語句(儲存程式最終是通過語句呼叫的,所以如果一個語句是由儲存程式呼叫的,雖然說這個語句事件是巢狀在儲存程式中的,但是實際上對於事件型別來講,仍然是巢狀在語句事件中),這些列包含有關父語句的資訊。如果不是巢狀語句或者是父語句本身產生的事件,則這些列值為 NULL  

OBJECT_INSTANCE_BEGIN: 語句的唯一標識,該列值是記憶體中物件的地址 

MYSQL_ERRNO: 語句執行的錯誤號,此值來自程式碼區域的語句診斷區域

RETURNED_SQLSTATE: 語句執行的 SQLSTATE 值,此值來自程式碼區域的語句診斷區域 

MESSAGE_TEXT: 語句執行的具體錯誤資訊,此值來自程式碼區域的語句診斷區域 

ERRORS: 語句執行是否發生錯誤。如果 SQLSTATE 值以 00 (完成)或 01 (警告)開始,則該列值為0。其他任何 SQLSTATE 值時,該列值為1

WARNINGS :語句警告數,此值來自程式碼區域的語句診斷區域 

ROWS_AFFECTED :受該語句影響的行數。有關“受影響”的含義的描述,參見連線: https://dev.mysql.com/doc/refman/5.7/en/mysql-affected-rows.html  

  • 使用mysql_query()或mysql_real_query()函式執行語句後,可能會立即呼叫mysql_affected_rows()函式。如果是UPDATE,DELETE或INSERT,則返回最後一條語句更改、刪除、插入的行數。對於SELECT語句,mysql_affected_rows()的工作方式與mysql_num_rows()一樣(在執行結果最後返回的資訊中看不到effected統計資訊) 

  • 對於UPDATE語句,受影響的行值預設為實際更改的行數。如果在連線到mysqld時指定了CLIENT_FOUND_ROWS標誌給mysql_real_connect()函式,那麼affected-rows的值是“found”的行數。即WHERE子句匹配到的行數 

  • 對於REPLACE語句,如果發生新舊行替換操作,則受影響的行值為2,因為在這種情況下,實際上是先刪除舊值,後插入新值兩個行操作 

  • 對於INSERT … ON DUPLICATE KEY UPDATE語句,如果行作為新行插入,則每行的affected計數為1,如果發生舊行更新為新行則每行affected計數為2,如果沒有發生任何插入和更新,則每行的affected計數為0 (但如果指定了CLIENT_FOUND_ROWS標誌,則沒有發生任何的插入和更新時,即set值就為當前的值時,每行的受影響行值計數為1而不是0) 

  • 在儲存過程的CALL語句呼叫之後,mysql_affected_rows()返回的影響行數是儲存程式中的最後一個語句執行的影響行數值,如果該語句返回-1,則儲存程式最終返回0受影響。所以在儲存程式執行時返回的影響行數並不可靠,但是你可以自行在儲存程式中實現一個計數器變數在SQL級別使用ROW_COUNT()來獲取各個語句的受影響的行值並相加,最終通過儲存程式返回這個變數值。 

  • 在MySQL 5.7中,mysql_affected_rows()為更多的語句返回一個有意義的值。 

* 1)、對於DDL語句,row_count()函式返回0,例如:CREATE TABLE、ALTER TABLE、DROP TABLE之類的語句 

* 2)、對於除SELECT之外的DML語句:row_count()函式返回實際資料變更的行數。例如:UPDATE、INSERT、DELETE語句,現在也適用於LOAD DATA INFILE之類的語句,大於0的返回值表示DML語句做了資料變更,如果返回為0,則表示DML語句沒有做任何資料變更,或者沒有與where子句匹配的記錄,如果返回-1則表示語句返回了錯誤 

* 3)、對於SELECT語句:row_count()函式返回-1,例如:SELECT * FROM t1語句,ROW_COUNT()返回-1(對於select語句,在呼叫mysql_store_result()之前呼叫了mysql_affected_rows()返回了)。但是對於SELECT * FROM t1 INTO OUTFILE'file_name'這樣的語句,ROW_COUNT()函式將返回實際寫入檔案中的資料行數 

* 4)、對於SIGNAL語句:row_count()函式返回0 

* 5)、因為mysql_affected_rows()返回的是一個無符號值,所以row_count()函式返回值小於等於0時都轉換為0值返回或者不返回給effected值,row_count()函式返回值大於0時則返回給effected值

ROWS_SENT:語句返回給客戶端的資料行數 

ROWS_EXAMINED:在執行語句期間從儲存引擎讀取的資料行數

CREATED_TMP_DISK_TABLES:像Created_tmp_disk_tables狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別 

CREATED_TMP_TABLES:像Created_tmp_tables狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別 

SELECT_FULL_JOIN:像Select_full_join狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別 

SELECT_FULL_RANGE_JOIN:像Select_full_range_join狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別 

SELECT_RANGE:就像Select_range狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別 

SELECT_RANGE_CHECK:像Select_range_check狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別 

SELECT_SCAN:像Select_scan狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別 

SORT_MERGE_PASSES:像Sort_merge_passes狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別 

SORT_RANGE:像Sort_range狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別 

SORT_ROWS:像Sort_rows狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別

SORT_SCAN:像Sort_scan狀態變數一樣的計數值,但是這裡只用於這個事件中的語句統計而不針對全域性、會話級別

NO_INDEX_USED:如果語句執行表掃描而不使用索引,則該列值為1,否則為0 

NO_GOOD_INDEX_USED:如果伺服器找不到用於該語句的合適索引,則該列值為1,否則為0

NESTING_EVENT_ID,NESTING_EVENT_TYPE,NESTING_EVENT_LEVEL:這三列與其他列結合一起使用,為頂級(未知抽象的語句或者說是父語句)語句和巢狀語句(在儲存的程式中執行的語句)提供以下事件資訊 

  • 對於頂級語句:

OBJECT_TYPE = NULL,OBJECT_SCHEMA = NULL,OBJECT_NAME = NULL,NESTING_EVENT_ID = NULL,NESTING_EVENT_TYPE = NULL,NESTING_LEVEL = 0 

  • 對於巢狀語句:OBJECT_TYPE =父語句物件型別,OBJECT_SCHEMA =父語句資料庫級名稱,OBJECT_NAME =父語句表級物件名稱,NESTING_EVENT_ID =父語句EVENT_ID,NESTING_EVENT_TYPE ='STATEMENT',NESTING_LEVEL =父語句NESTING_LEVEL加一,例如:1,表示父語句的下一層巢狀語句 

允許使用TRUNCATE TABLE語句


events_statements_history 表


events_statements_history表包含每個執行緒最新的N個語句事件。 在server啟動時,N的值會自動調整。 要顯式設定N的大小,可以在server啟動之前設定系統變數 performance_schema_events_statements_history_size 的值。 statement事件執行完成時才會新增到該表中。 當新增新事件到該表時,如果對應執行緒的事件在該表中的配額已滿,則會丟棄對應執行緒的較舊的事件

events_statements_history與events_statements_current表結構相同 

PS:允許使用TRUNCATE TABLE語句


events_statements_history_long 表


events_statements_history_long表包含最近的N個語句事件。在server啟動時,N的值會自動調整。 要顯式設定N的大小,可以在server啟動之前設定系統變數performance_schema_events_statements_history_long_size的值。 statement事件需要執行結束時才會新增到該表中。 當新增新事件到該表時,如果該表的全域性配額已滿,則會丟棄該表中較舊的事件 

events_statements_history_long與events_statements_current表結構相同

PS:允許使用TRUNCATE TABLE語句


事務事件表


事務事件記錄表與等待事件記錄表一樣,也有三張表,這些表記錄了當前與最近在MySQL例項中發生了哪些事務事件,時間消耗是多少

要注意:事務事件相關配置中,setup_instruments表中只有一個名為transaction的instrument,預設關閉,setup_consumers表中transactions相關的consumers配置預設關閉了


events_transactions_current 表


events_transactions_current表包含當前事務事件資訊,每個執行緒只保留一行最近事務的事務事件 

在包含事務事件資訊的表中,events_transactions_current是基礎表。其他包含事務事件資訊的表中的資料邏輯上來源於當前事件表。例如:events_transactions_history和events_transactions_history_long表分別包含每個執行緒最近10行事務事件資訊和全域性最近10000行事務事件資訊

表記錄內容示例(以下資訊來自對某表執行了一次select等值查詢的事務事件資訊)


root@localhost : performance

_schema 12:50:10>  select * from events_

transactions_current\G;

*************************** 1. row ***************************
                 THREAD_ID: 46
                 EVENT_ID: 38685
             END_EVENT_ID: 38707
               EVENT_NAME: transaction
                     STATE: COMMITTED
                   TRX_ID: 422045139261264
                     GTID: AUTOMATIC
             XID_FORMAT_ID: NULL
                 XID_GTRID: NULL
                 XID_BQUAL: NULL
                 XA_STATE: NULL
                   SOURCE: handler.cc:1421
               TIMER_START: 16184509764409000
                 TIMER_END: 16184509824175000
               TIMER_WAIT: 59766000
               ACCESS_MODE: READ WRITE
           ISOLATION_LEVEL: READ COMMITTED
               AUTOCOMMIT: YES
     NUMBER_OF_SAVEPOINTS: 0
NUMBER _OF_ ROLLBACK _TO_ SAVEPOINT: 0
NUMBER _OF_ RELEASE_SAVEPOINT: 0
     OBJECT_INSTANCE_BEGIN: NULL
         NESTING_EVENT_ID: 38667
       NESTING_EVENT_TYPE: STATEMENT
1 row in set (0.00 sec)


以上的輸出結果與語句的等待事件形式類似,這裡不再贅述, events_transactions_current 表完整欄位含義如下:

THREAD_ID,EVENT_ID :與事件關聯的執行緒號和事件啟動時的事件編號,可以使用 THREAD_ID和EVENT_ID 列值來唯一標識該行,這兩行的值作為組合條件時不會出現相同的資料行

END_EVENT_ID:當一個事件開始執行時,對應行記錄的該列值被設定為NULL,當一個事件執行結束時,對應的行記錄的該列值被更新為該事件的ID

EVENT_NAME:收集該事務事件的instruments的名稱。來自setup_instruments表的NAME列值

STATE:當前事務狀態。有效值為:ACTIVE(執行了START TRANSACTION或BEGIN語句之後,事務未提交或未回滾之前)、COMMITTED(執行了COMMIT之後)、ROLLED BACK(執行了ROLLBACK語句之後)

TRX_ID:未使用,欄位值總是為NULL

GTID:包含gtid_next系統變數的值,其值可能是格式為:UUID:NUMBER的GTID,也可能是:ANONYMOUS、AUTOMATIC。對於AUTOMATIC列值的事務事件,GTID列在事務提交和對應事務的GTID實際分配時都會進行更改(如果gtid_mode系統變數為ON或ON_PERMISSIVE,則GTID列將更改為事務的GTID,如果gtid_mode為OFF或OFF_PERMISSIVE,則GTID列將更改為ANONYMOUS)

XID_FORMAT_ID,XID_GTRID和XID_BQUAL :XA事務識別符號的元件。關於XA事務語法詳見連結: https://dev.mysql.com/doc/refman/5.7/en/xa-statements.html

XA_STATE:XA事務的狀態。有效值為:ACTIVE(執行了XA START之後,未執行其他後續XA語句之前)、IDLE(執行了XA END語句之後,未執行其他後續XA語句之前)、PREPARED(執行了XA PREPARE語句之後,未執行其他後續XA語句之前)、ROLLED BACK(執行了XA ROLLBACK語句之後,未執行其他後續XA語句之前)、COMMITTED(執行了XA COMMIT語句之後)

SOURCE: 原始檔的名稱及其用於檢測該事件的程式碼位於原始檔中的行號,您可以檢查原始碼來確定涉及的程式碼

TIMER_START,TIMER_END,TIMER_WAIT :事件的時間資訊。這些值的單位是皮秒(萬億分之一秒)。  TIMER_START和TIMER_END 值表示事件的開始時間和結束時間。 TIMER_WAIT 是事件執行消耗的時間(持續時間) 

  • 如果事件未執行完成,則TIMER_END為當前時間,TIMER_WAIT為當前為止所經過的時間(TIMER_END - TIMER_START)

  • 如果監視儀器配置表setup_instruments中對應的監視器TIMED欄位被設定為 NO,則不會收集該監視器的時間資訊,那麼對於該事件採集的資訊記錄中,TIMER_START,TIMER_END和TIMER_WAIT欄位值均為NULL  

ACCESS_MODE :事務訪問模式。有效值為: READ ONLY或READ WRITE

ISOLATION_LEVEL: 事務隔離級別。有效值為: REPEATABLE READ、READ COMMITTED、READ UNCOMMITTED、SERIALIZABLE

AUTOCOMMIT:在事務開始時是否啟用了自動提交模式,如果啟用則為YES,沒有啟用則為NO 

NUMBER_OF_SAVEPOINTS,NUMBER_OF_ROLLBACK_TO_SAVEPOINT,NUMBER_OF_RELEASE_SAVEPOINT:在事務內執行的SAVEPOINT,ROLLBACK TO SAVEPOINT和RELEASE SAVEPOINT語句的數量

OBJECT_INSTANCE_BEGIN:未使用,欄位值總是為NULL

NESTING_EVENT_ID:巢狀事務事件的父事件EVENT_ID值

NESTING_EVENT_TYPE: 巢狀事件型別。有效值為: TRANSACTION、STATEMENT、STAGE、WAIT  (由於事務無法巢狀,因此該列值不會出現 TRANSACTION)  

允許使用TRUNCATE TABLE語句


events_transactions_history 表


events_transactions_history表包含每個執行緒最近的N個事務事件。 在server啟動時,N的值會自動調整。 要顯式設定N的大小,可以在server啟動之前設定系統變數

performance_schema_events_transactions_history_size 的值。事務事件未執行完成之前不會新增到該表中。當有新的事務事件新增到該表時,如果該表已滿,則會丟棄對應執行緒較舊的事務事件

events_transactions_history與events_transactions_current 表結構相同

PS:允許使用TRUNCATE TABLE語句


events_transactions_history_long 表


events_transactions_history_long表包含全域性最近的N個事務事件。在server啟動時,N的值會自動調整。 要顯式設定N的大小,可以在server啟動之前設定系統變數

performance_schema_events_transactions_history_long_size 的值。事務事件在執行完之前不會新增到該表中。當新增新事務事件時,如果該表已滿,則會丟棄較舊的事件 

events_transactions_history_long與events_transactions_current 表結構相同

PS:允許使用TRUNCATE TABLE語句


現在,相信大家已經比較清楚什麼是事件了,但有時候我們不需要知道每時每刻產生的每一條事件記錄資訊, 例如:我們希望瞭解資料庫執行以來一段時間的事件統計資料,這個時候就需要檢視事件統計表了。今天將帶領大家一起踏上系列第四篇的征程(全系共7個篇章),在這一期裡,我們將為大家全面講解performance_schema中事件統計表。統計事件表分為5個類別,分別為等待事件、階段事件、語句事件、事務事件、記憶體事件。下面,請跟隨我們一起開始performance_schema系統的學習之旅吧。


|  等待事件統計表

performance_schema把等待事件統計表按照不同的分組列(不同緯度)對等待事件相關的資料進行聚合(聚合統計資料列包括:事件發生次數,總等待時間,最小、最大、平均等待時間),注意:等待事件的採集功能有一部分預設是禁用的,需要的時候可以通過setup_instruments和setup_objects表動態開啟,等待事件統計表包含如下幾張表:


admin@localhost : performance_schema 

06:17:11

> show tables like 

'%events_waits_summary%'

;

+-------------------------------------------------------+
| Tables_in_performance_schema (%events_waits_summary%) |
+-------------------------------------------------------+
| events_waits_summary_by_account_by_event_name        |
| events_waits_summary_by_host_by_event_name            |
| events_waits_summary_by_instance                      |
| events_waits_summary_by_thread_by_event_name          |
| events_waits_summary_by_user_by_event_name            |
| events_waits_summary_global_by_event_name            |
+-------------------------------------------------------+
6 rows in set ( . 00 sec)


我們先來看看這些表中記錄的統計資訊是什麼樣子的。



# events_waits_summary_by_account_by_event_name表


root@localhost : performance _schema 11:07:09> select * from events_ waits _summary_ by _account_ by _event_ name limit 1\G
*************************** 1. row ***************************
     USER: NULL
     HOST: NULL
EVENT _NAME: wait/synch/mutex/sql/TC_ LOG _MMAP::LOCK_ tc
COUNT_STAR: 0
SUM _TIMER_ WAIT: 0
MIN _TIMER_ WAIT: 0
AVG _TIMER_ WAIT: 0
MAX _TIMER_ WAIT: 0
1 row in set (0.00 sec)
# events_waits_summary_by_host_by_event_name表
root@localhost : performance _schema 11:07:14> select * from events_ waits _summary_ by _host_ by _event_ name limit 1\G
*************************** 1. row ***************************
     HOST: NULL
EVENT _NAME: wait/synch/mutex/sql/TC_ LOG _MMAP::LOCK_ tc
COUNT_STAR: 0
SUM _TIMER_ WAIT: 0
MIN _TIMER_ WAIT: 0
AVG _TIMER_ WAIT: 0
MAX _TIMER_ WAIT: 0
1 row in set (0.00 sec)
# events_waits_summary_by_instance表
root@localhost : performance _schema 11:08:05> select * from events_ waits _summary_ by_instance limit 1\G
*************************** 1. row ***************************
      EVENT_NAME: wait/synch/mutex/mysys/THR_LOCK_heap
OBJECT _INSTANCE_ BEGIN: 32492032
      COUNT_STAR: 0
  SUM _TIMER_ WAIT: 0
  MIN _TIMER_ WAIT: 0
  AVG _TIMER_ WAIT: 0
  MAX _TIMER_ WAIT: 0
1 row in set (0.00 sec)
# events_waits_summary_by_thread_by_event_name表
root@localhost : performance _schema 11:08:23> select * from events_ waits _summary_ by _thread_ by _event_ name limit 1\G
*************************** 1. row ***************************
THREAD_ID: 1
EVENT _NAME: wait/synch/mutex/sql/TC_ LOG _MMAP::LOCK_ tc
COUNT_STAR: 0
SUM _TIMER_ WAIT: 0
MIN _TIMER_ WAIT: 0
AVG _TIMER_ WAIT: 0
MAX _TIMER_ WAIT: 0
1 row in set (0.00 sec)
# events_waits_summary_by_user_by_event_name表
root@localhost : performance _schema 11:08:36> select * from events_ waits _summary_ by _user_ by _event_ name limit 1\G
*************************** 1. row ***************************
     USER: NULL
EVENT _NAME: wait/synch/mutex/sql/TC_ LOG _MMAP::LOCK_ tc
COUNT_STAR: 0
SUM _TIMER_ WAIT: 0
MIN _TIMER_ WAIT: 0
AVG _TIMER_ WAIT: 0
MAX _TIMER_ WAIT: 0
1 row in set (0.00 sec)
# events_waits_summary_global_by_event_name表
root@localhost : performance _schema 11:08:53> select * from events_ waits _summary_ global _by_ event_name limit 1\G
*************************** 1. row ***************************
EVENT _NAME: wait/synch/mutex/sql/TC_ LOG _MMAP::LOCK_ tc
COUNT_STAR: 0
SUM _TIMER_ WAIT: 0
MIN _TIMER_ WAIT: 0
AVG _TIMER_ WAIT: 0
MAX _TIMER_ WAIT: 0
1 row in set (0.00 sec)


從上面表中的示例記錄資訊中,我們可以看到:

每個表都有各自的一個或多個分組列,以確定如何聚合事件資訊(所有表都有EVENT_NAME列,列值與setup_instruments表中NAME列值對應),如下: 

events_waits_summary_by_account_by_event_name表:按照列EVENT_NAME、USER、HOST進行分組事件資訊 

events_waits_summary_by_host_by_event_name表:按照列EVENT_NAME、HOST進行分組事件資訊 

events_waits_summary_by_instance表:按照列EVENT_NAME、OBJECT_INSTANCE_BEGIN進行分組事件資訊。如果一個instruments(event_name)建立有多個例項,則每個例項都具有唯一的OBJECT_INSTANCE_BEGIN值,因此每個例項會進行單獨分組 

events_waits_summary_by_thread_by_event_name表:按照列THREAD_ID、EVENT_NAME進行分組事件資訊 

events_waits_summary_by_user_by_event_name表:按照列EVENT_NAME、USER進行分組事件資訊 

events_waits_summary_global_by_event_name表:按照EVENT_NAME列進行分組事件資訊

所有表的統計列(數值型)都為如下幾個:  

COUNT_STAR:事件被執行的數量。此值包括所有事件的執行次數,需要啟用等待事件的instruments 

SUM_TIMER_WAIT:統計給定計時事件的總等待時間。此值僅針對有計時功能的事件instruments或開啟了計時功能事件的instruments,如果某事件的instruments不支援計時或者沒有開啟計時功能,則該欄位為NULL。其他xxx_TIMER_WAIT欄位值類似 

MIN_TIMER_WAIT:給定計時事件的最小等待時間 

AVG_TIMER_WAIT:給定計時事件的平均等待時間 

MAX_TIMER_WAIT:給定計時事件的最大等待時間

PS:等待事件統計表允許使用TRUNCATE TABLE語句。

執行該語句時有如下行為:

對於未按照帳戶、主機、使用者聚合的統計表,truncate語句會將統計列值重置為零,而不是刪除行。

對於按照帳戶、主機、使用者聚合的統計表,truncate語句會刪除已開端連線的帳戶,主機或使用者對應的行,並將其他有連線的行的統計列值重置為零(實測跟未按照帳號、主機、使用者聚合的統計表一樣,只會被重置不會被刪除)。

此外,按照帳戶、主機、使用者、執行緒聚合的每個等待事件統計表或者events_waits_summary_global_by_event_name表,如果依賴的連線表(accounts、hosts、users表)執行truncate時,那麼依賴的這些表中的統計資料也會同時被隱式truncate 。

注意: 這些表只針對等待事件資訊進行統計,即包含setup_instruments表中的wait/%開頭的採集器+ idle空閒採集器,每個等待事件在每個表中的統計記錄行數需要看如何分組(例如:按照使用者分組統計的表中,有多少個活躍使用者,表中就會有多少條相同採集器的記錄),另外,統計計數器是否生效還需要看setup_instruments表中相應的等待事件採集器是否啟用。


|  階段事件統計表

performance_schema把階段事件統計表也按照與等待事件統計表類似的規則進行分類聚合,階段事件也有一部分是預設禁用的,一部分是開啟的,階段事件統計表包含如下幾張表:


admin@localhost : performance_schema 

06:23:02

> show tables like 

'%events_stages_summary%'

;

+--------------------------------------------------------+
| Tables_in_performance_schema (%events_stages_summary%) |
+--------------------------------------------------------+
| events_stages_summary_by_account_by_event_name        |
| events_stages_summary_by_host_by_event_name            |
| events_stages_summary_by_thread_by_event_name          |
| events_stages_summary_by_user_by_event_name            |
| events_stages_summary_global_by_event_name            |
+--------------------------------------------------------+
5 rows in set ( . 00 sec)


我們先來看看這些表中記錄的統計資訊是什麼樣子的。



# events_stages_summary_by_account_by_event_name表


root@localhost : performance _schema 11:21:04> select * from events_ stages _summary_ by _account_ by _event_ name where USER is not null limit 1\G
*************************** 1. row ***************************
     USER: root
     HOST: localhost
EVENT_NAME: stage/sql/After create
COUNT_STAR: 0
SUM _TIMER_ WAIT: 0
MIN _TIMER_ WAIT: 0
AVG _TIMER_ WAIT: 0
MAX _TIMER_ WAIT: 0
1 row in set (0.01 sec)
# events_stages_summary_by_host_by_event_name表
root@localhost : performance _schema 11:29:27> select * from events_ stages _summary_ by _host_ by _event_ name where HOST is not null limit 1\G
*************************** 1. row ***************************
     HOST: localhost
EVENT_NAME: stage/sql/After create
COUNT_STAR: 0
SUM _TIMER_ WAIT: 0
MIN _TIMER_ WAIT: 0
AVG _TIMER_ WAIT: 0
MAX _TIMER_ WAIT: 0
1 row in set (0.00 sec)
# events_stages_summary_by_thread_by_event_name表
root@localhost : performance _schema 11:37:03> select * from events_ stages _summary_ by _thread_ by _event_ name where thread_id is not null limit 1\G
*************************** 1. row ***************************
THREAD_ID: 1
EVENT_NAME: stage/sql/After create
COUNT_STAR: 0
SUM _TIMER_ WAIT: 0
MIN _TIMER_ WAIT: 0
AVG _TIMER_ WAIT: 0
MAX _TIMER_ WAIT: 0
1 row in set (0.01 sec)
# events_stages_summary_by_user_by_event_name表
root@localhost : performance _schema 11:42:37> select * from events_ stages _summary_ by _user_ by _event_ name where user is not null limit 1\G
*************************** 1. row ***************************
     USER: root
EVENT_NAME: stage/sql/After create
COUNT_STAR: 0
SUM _TIMER_ WAIT: 0
MIN _TIMER_ WAIT: 0
AVG _TIMER_ WAIT: 0
MAX _TIMER_ WAIT: 0
1 row in set (0.00 sec)
# events_stages_summary_global_by_event_name表
root@localhost : performance _schema 11:43:03> select * from events_ stages _summary_ global _by_ event_name limit 1\G
*************************** 1. row ***************************
EVENT_NAME: stage/sql/After create
COUNT_STAR: 0
SUM _TIMER_ WAIT: 0
MIN _TIMER_ WAIT: 0
AVG _TIMER_ WAIT: 0
MAX _TIMER_ WAIT: 0
1 row in set (0.00 sec)


從上面表中的示例記錄資訊中,我們可以看到,同樣與等待事件類似,按照使用者、主機、使用者+主機、執行緒等緯度進行分組與統計的列,這些列的含義與等待事件類似,這裡不再贅述。

注意: 這些表只針對階段事件資訊進行統計,即包含setup_instruments表中的stage/%開頭的採集器,每個階段事件在每個表中的統計記錄行數需要看如何分組(例如:按照使用者分組統計的表中,有多少個活躍使用者,表中就會有多少條相同採集器的記錄),另外,統計計數器是否生效還需要看setup_instruments表中相應的階段事件採集器是否啟 用。

PS: 對這些表使用truncate語句,影響與等待事件類似。


|  事務事件統計表

performance_schema把事務事件統計表也按照與等待事件統計表類似的規則進行分類統計,事務事件instruments只有一個transaction,預設禁用,事務事件統計表有如下幾張表:


admin@localhost : performance_schema 

06:37:45

> show tables like 

'%events_transactions_summary%'

;

+--------------------------------------------------------------+
| Tables_in_performance_schema (%events_transactions_summary%) |
+--------------------------------------------------------------+
| events_transactions_summary_by_account_by_event_name        |
| events_transactions_summary_by_host_by_event_name            |
| events_transactions_summary_by_thread_by_event_name          |
| events_transactions_summary_by_user_by_event_name            |
| events_transactions_summary_global_by_event_name            |
+--------------------------------------------------------------+
5 rows in set ( . 00 sec)


我們先來看看這些表中記錄的統計資訊是什麼樣子的(由於單行記錄較長,這裡只列出events_transactions_summary_by_account_by_event_name表中的示例資料,其餘 的示例資料省略掉部分相同欄位)。



# events_transactions_summary_by_account_by_event_name表


root@localhost : performance _schema 01:19:07> select * from events_ transactions _summary_ by _account_ by _event_ name where COUNT_STAR!=0 limit 1\G
*************************** 1. row ***************************
           USER: root
           HOST: localhost
     EVENT_NAME: transaction
     COUNT_STAR: 7
 SUM _TIMER_ WAIT: 8649707000
 MIN _TIMER_ WAIT: 57571000
 AVG _TIMER_ WAIT: 1235672000
 MAX _TIMER_ WAIT: 2427645000
COUNT _READ_ WRITE: 6
SUM _TIMER_ READ_WRITE: 8592136000
MIN _TIMER_ READ_WRITE: 87193000
AVG _TIMER_ READ_WRITE: 1432022000
MAX _TIMER_ READ_WRITE: 2427645000
COUNT _READ_ ONLY: 1
SUM _TIMER_ READ_ONLY: 57571000
MIN _TIMER_ READ_ONLY: 57571000
AVG _TIMER_ READ_ONLY: 57571000
MAX _TIMER_ READ_ONLY: 57571000
1 row in set (0.00 sec)
# events_transactions_summary_by_host_by_event_name表
root@localhost : performance _schema 01:25:13> select * from events_ transactions _summary_ by _host_ by _event_ name where COUNT_STAR!=0 limit 1\G
*************************** 1. row ***************************
           HOST: localhost
     EVENT_NAME: transaction
     COUNT_STAR: 7
......
1 row in set (0.00 sec)
# events_transactions_summary_by_thread_by_event_name表
root@localhost : performance _schema 01:25:27> select * from events_ transactions _summary_ by _thread_ by _event_ name where SUM _TIMER_ WAIT!=0\G
*************************** 1. row ***************************
      THREAD_ID: 46
     EVENT_NAME: transaction
     COUNT_STAR: 7
......
1 row in set (0.00 sec)
# events_transactions_summary_by_user_by_event_name表
root@localhost : performance _schema 01:27:27> select * from events_ transactions _summary_ by _user_ by _event_ name where SUM _TIMER_ WAIT!=0\G
*************************** 1. row ***************************
           USER: root
     EVENT_NAME: transaction
     COUNT_STAR: 7
......
1 row in set (0.00 sec)
# events_transactions_summary_global_by_event_name表
root@localhost : performance _schema 01:27:32> select * from events_ transactions _summary_ global _by_ event _name where SUM_ TIMER_WAIT!=0\G
*************************** 1. row ***************************
     EVENT_NAME: transaction
     COUNT_STAR: 7
......
1 row in set (0.00 sec)


從上面表中的示例記錄資訊中,我們可以看到,同樣與等待事件類似,按照使用者、主機、使用者+主機、執行緒等緯度進行分組與統計的列,這些列的含義與等待事件類似,這裡不再贅述,但對於事務統計事件,針對讀寫事務和只讀事務還單獨做了統計(xx_READ_WRITE和xx_READ_ONLY列,只讀事務需要設定只讀事務變數transaction_read_only=on才會進行統計)。

注意: 這些表只針對事務事件資訊進行統計,即包含且僅包含setup_instruments表中的transaction採集器,每個事務事件在每個表中的統計記錄行數需要看如何分組(例如:按照使用者分組統計的表中,有多少個活躍使用者,表中就會有多少條相同採集器的記錄),另外,統計計數器是否生效還需要看transaction採集器是否啟用。

事務聚合統計規則 

* 事務事件的收集不考慮隔離級別,訪問模式或自動提交模式 

* 讀寫事務通常比只讀事務佔用更多資源,因此事務統計表包含了用於讀寫和只讀事務的單獨統計列 

* 事務所佔用的資源需求多少也可能會因事務隔離級別有所差異(例如:鎖資源)。但是:每個server可能是使用相同的隔離級別,所以不單獨提供隔離級別相關的統計列

PS: 對這些表使用truncate語句,影響與等待事件類似。


|  語句事件統計表

performance_schema把語句事件統計表也按照與等待事件統計表類似的規則進行分類統計,語句事件instruments預設全部開啟,所以,語句事件統計表中預設會記錄所有的語句事件統計資訊, 語句事件統計表包含如下幾張表:

events_statements_summary_by_account_by_event_name:按照每個帳戶和語句事件名稱進行統計

events_statements_summary_by_digest:按照每個庫級別物件和語句事件的原始語句文字統計值(md5 hash字串)進行統計,該統計值是基於事件的原始語句文字進行精煉(原始語句轉換為標準化語句),每行資料中的相關數值欄位是具有相同統計值的統計結果。

events_statements_summary_by_host_by_event_name:按照每個主機名和事件名稱進行統計的Statement事件

events_statements_summary_by_program:按照每個儲存程式(儲存過程和函式,觸發器和事件)的事件名稱進行統計的Statement事件

events_statements_summary_by_thread_by_event_name:按照每個執行緒和事件名稱進行統計的Statement事件

events_statements_summary_by_user_by_event_name:按照每個使用者名稱和事件名稱進行統計的Statement事件

events_statements_summary_global_by_event_name:按照每個事件名稱進行統計的Statement事件

prepared_statements_instances:按照每個prepare語句例項聚合的統計資訊

可通過如下語句檢視語句事件統計表:


admin@localhost : performance_schema 

06:27:58

> show tables like 

'%events_statements_summary%'

;

+------------------------------------------------------------+
| Tables_in_performance_schema (%events_statements_summary%) |
+------------------------------------------------------------+
| events_statements_summary_by_account_by_event_name        |
| events_statements_summary_by_digest                        |
| events_statements_summary_by_host_by_event_name            |
| events_statements_summary_by_program                      |
| events_statements_summary_by_thread_by_event_name          |
| events_statements_summary_by_user_by_event_name            |
| events_statements_summary_global_by_event_name            |
+------------------------------------------------------------+
7 rows in set ( . 00 sec)
admin@localhost : performance_schema 06:28:48 > show tables like '%prepare%' ;
+------------------------------------------+
| Tables_in_performance_schema (%prepare%) |
+------------------------------------------+
| prepared_statements_instances            |
+------------------------------------------+
1 row in set ( . 00 sec)


我們先來看看這些表中記錄的統計資訊是什麼樣子的(由於單行記錄較長,這裡只列出events_statements_summary_by_account_by_event_name 表中的示例資料,其餘 的示例資料省略掉部分相同欄位)。



# events_statements_summary_by_account_by_event_name表


root@localhost : performance _schema 10:37:27> select * from events_ statements _summary_ by _account_ by _event_ name where COUNT_STAR!=0 limit 1\G
*************************** 1. row ***************************
                  USER: root
                  HOST: localhost
            EVENT_NAME: statement/sql/select
            COUNT_STAR: 53
        SUM_TIMER_WAIT: 234614735000
        MIN_TIMER_WAIT: 72775000
        AVG_TIMER_WAIT: 4426693000
        MAX_TIMER_WAIT: 80968744000
         SUM_LOCK_TIME: 26026000000
            SUM_ERRORS: 2
          SUM_WARNINGS: 0
     SUM_ROWS_AFFECTED: 0
         SUM_ROWS_SENT: 1635
     SUM_ROWS_EXAMINED: 39718
SUM _CREATED_ TMP _DISK_ TABLES: 3
SUM _CREATED_ TMP_TABLES: 10
  SUM _SELECT_ FULL_JOIN: 21
SUM _SELECT_ FULL _RANGE_ JOIN: 0
      SUM_SELECT_RANGE: 0
SUM _SELECT_ RANGE_CHECK: 0
       SUM_SELECT_SCAN: 45
 SUM _SORT_ MERGE_PASSES: 0
        SUM_SORT_RANGE: 0
         SUM_SORT_ROWS: 170
         SUM_SORT_SCAN: 6
     SUM_NO_INDEX_USED: 42
SUM _NO_ GOOD _INDEX_ USED: 0
1 row in set (0.00 sec)
# events_statements_summary_by_digest表
root@localhost : performance _schema 11:01:51> select * from events_ statements _summary_ by_digest limit 1\G
*************************** 1. row ***************************
           SCHEMA_NAME: NULL
                DIGEST: 4fb483fe710f27d1d06f83573c5ce11c
           DIGEST_TEXT: SELECT @@`version_comment` LIMIT ?
            COUNT_STAR: 3
......
            FIRST_SEEN: 2018-05-19 22:33:50
             LAST_SEEN: 2018-05-20 10:24:42
1 row in set (0.00 sec)
# events_statements_summary_by_host_by_event_name表
root@localhost : performance _schema 11:02:15> select * from events_ statements _summary_ by _host_ by _event_ name where COUNT_STAR!=0 limit 1\G
*************************** 1. row ***************************
                  HOST: localhost
            EVENT_NAME: statement/sql/select
            COUNT_STAR: 55
......
1 row in set (0.00 sec)
# events_statements_summary_by_program表(需要呼叫了儲存過程或函式之後才會有資料)
root@localhost : performance _schema 12:34:43> select * from events_ statements _summary_ by_program\G;
*************************** 1. row ***************************
           OBJECT_TYPE: PROCEDURE
         OBJECT_SCHEMA: sys
           OBJECT_NAME: ps_setup_enable_consumer
           COUNT_STAR: 1
............
1 row in set (0.00 sec)
# events_statements_summary_by_thread_by_event_name表
root@localhost : performance _schema 11:03:19> select * from events_ statements _summary_ by _thread_ by _event_ name where COUNT_STAR!=0 limit 1\G
*************************** 1. row ***************************
             THREAD_ID: 47
            EVENT_NAME: statement/sql/select
            COUNT_STAR: 11
......
1 row in set (0.01 sec)
# events_statements_summary_by_user_by_event_name表
root@localhost : performance _schema 11:04:10> select * from events_ statements _summary_ by _user_ by _event_ name where COUNT_STAR!=0 limit 1\G
*************************** 1. row ***************************
                  USER: root
            EVENT_NAME: statement/sql/select
            COUNT_STAR: 58
......
1 row in set (0.00 sec)
# events_statements_summary_global_by_event_name表
root@localhost : performance _schema 11:04:31> select * from events_ statements _summary_ global _by_ event_name limit 1\G
*************************** 1. row ***************************
            EVENT_NAME: statement/sql/select
            COUNT_STAR: 59
......
1 row in set (0.00 sec)


從上面表中的示例記錄資訊中,我們可以看到,同樣與等待事件類似,按照使用者、主機、使用者+主機、執行緒等緯度進行分組與統計的列,分組和部分時間統計列與等待事件類似,這裡不再贅述, 但對於語句統計事件,有針對語句物件的額外的統計列,如下:

SUM_xxx:針對events_statements_*事件記錄表中相應的xxx列進行統計。例如:語句統計表中的SUM_LOCK_TIME和SUM_ERRORS列對events_statements_current事件記錄表中LOCK_TIME和ERRORS列進行統計

events_statements_summary_by_digest表有自己額外的統計列:

* FIRST_SEEN,LAST_SEEN:顯示某給定語句第一次插入       events_statements_summary_by_digest表和最後一次更新該表的 時間戳

events_statements_summary_by_program表有自己額外的統計列:

* COUNT_STATEMENTS,SUM_STATEMENTS_WAIT,MIN_STATEMENTS_WAIT,AVG_STATEMENTS_WAIT,MAX_STATEMENTS_WAIT:關於儲存程式執行期間呼叫的巢狀語句的統計資訊

prepared_statements_instances表有自己額外的統計列:

* COUNT_EXECUTE,SUM_TIMER_EXECUTE,MIN_TIMER_EXECUTE,AVG_TIMER_EXECUTE,MAX_TIMER_EXECUTE:執行prepare語句物件的統計資訊


PS1:

關於events_statements_summary_by_digest表

如果setup_consumers配置表中statements_digest consumers啟用,則在語句執行完成時,將會把語句文字進行md5 hash計算之後 再傳送到events_statements_summary_by_digest表中。分組列基於該語句的DIGEST列值(md5 hash值) 

* 如果給定語句的統計資訊行在events_statements_summary_by_digest表中已經存在,則將該語句的統計資訊進行更新,並更新LAST_SEEN列值為當前時間 

* 如果給定語句的統計資訊行在events_statements_summary_by_digest表中沒有已存在行,並且events_statements_summary_by_digest表空間限制未滿的情況下,會在events_statements_summary_by_digest表中新插入一行統計資訊,FIRST_SEEN和LAST_SEEN列都使用當前時間 

* 如果給定語句的統計資訊行在events_statements_summary_by_digest表中沒有已存在行,且events_statements_summary_by_digest表空間限制已滿的情況下,則該語句的統計資訊將新增到DIGEST 列值為 NULL的特殊“catch-all”行,如果該特殊行不存在則新插入一行,FIRST_SEEN和LAST_SEEN列為當前時間。如果該特殊行已存在則更新該行的資訊,LAST_SEEN為當前時間

由於performance_schema表記憶體限制,所以維護了DIGEST = NULL的特殊行。 當events_statements_summary_by_digest表限制容量已滿的情況下,且新的語句統計資訊在需要插入到該表時又沒有在該表中找到匹配的DIGEST列值時,就會把這些語句統計資訊都統計到 DIGEST = NULL的行中。此行可幫助您估算events_statements_summary_by_digest表的限制是否需要調整

* 如果DIGEST = NULL行的COUNT_STAR列值佔據整個表中所有統計資訊的COUNT_STAR列值的比例大於0%,則表示存在由於該表限制已滿導致部分語句統計資訊無法分類儲存,如果你需要儲存所有語句的統計資訊,可以在server啟動之前調整系統變數performance_schema_digests_size的值,預設大小為200


PS2: 關於儲存程式監控行為:對於在setup_objects表中啟用了instruments的儲存程式型別,events_statements_summary_by_program將維護儲存程式的統計資訊,如下所示:

當某給定物件在server中首次被使用時(即使用call語句呼叫了儲存過程或自定義儲存函式時),將在events_statements_summary_by_program表中新增一行統計資訊;

當某給定物件被刪除時,該物件在events_statements_summary_by_program表中的統計資訊行將被刪除;

當某給定物件被執行時,其對應的統計資訊將記錄在events_statements_summary_by_program表中並進行統計。

 

PS3: 對這些表使用truncate語句,影響與等待事件類似。


|  記憶體事件統計表

performance_schema把記憶體事件統計表也按照與等待事件統計表類似的規則進行分類統計。

performance_schema會記錄記憶體使用情況並聚合記憶體使用統計資訊,如:使用的記憶體型別(各種快取,內部緩衝區等)和執行緒、帳號、使用者、主機的相關操作間接執行的記憶體操作。performance_schema從使用的記憶體大小、相關運算元量、高低水位(記憶體一次操作的最大和最小的相關統計值)。

記憶體大小統計資訊有助於瞭解當前server的記憶體消耗,以便及時進行記憶體調整。記憶體相關操作計數有助於瞭解當前server的記憶體分配器的整體壓力,及時掌握server效能資料。例如:分配單個位元組一百萬次與單次分配一百萬個位元組的效能開銷是不同的,通過跟蹤記憶體分配器分配的記憶體大小和分配次數就可以知道兩者的差異。

檢測記憶體工作負載峰值、記憶體總體的工作負載穩定性、可能的記憶體洩漏等是至關重要的。

記憶體事件instruments中除了performance_schema自身記憶體分配相關的事件instruments配置預設開啟之外,其他的記憶體事件instruments配置都預設關閉的,且在setup_consumers表中沒有像等待事件、階段事件、語句事件與事務事件那樣的單獨配置項。

PS: 記憶體統計表不包含計時資訊,因為記憶體事件不支援時間資訊收集。

 

記憶體事件統計表有如下幾張表:


admin@localhost : performance_schema 

06:56:56

> show tables like 

'%memory%summary%'

;

+-------------------------------------------------+
| Tables_in_performance_schema (%memory%summary%) |
+-------------------------------------------------+
| memory_summary_by_account_by_event_name        |
| memory_summary_by_host_by_event_name            |
| memory_summary_by_thread_by_event_name          |
| memory_summary_by_user_by_event_name            |
| memory_summary_global_by_event_name            |
+-------------------------------------------------+
5 rows in set ( . 00 sec)


我們先來看看這些表中記錄的統計資訊是什麼樣子的(由於單行記錄較長,這裡只列出memory_summary_by_account_by_event_name 表中的示例資料,其餘 的示例資料省略掉部分相同欄位)。



# 如果需要統計記憶體事件資訊,需要開啟記憶體事件採集器


root@localhost : performance _schema 11:50:46> update setup_ instruments set enabled='yes',timed='yes' where name like 'memory/%';
Query OK, 377 rows affected (0.00 sec)
Rows matched: 377 Changed: 377 Warnings: 0
# memory_summary_by_account_by_event_name表
root@localhost : performance _schema 11:53:24> select * from memory_ summary _by_ account _by_ event _name where COUNT_ ALLOC!=0 limit 1\G
*************************** 1. row ***************************
                   USER: NULL
                   HOST: NULL
             EVENT_NAME: memory/innodb/fil0fil
            COUNT_ALLOC: 103
             COUNT_FREE: 103
SUM _NUMBER_ OF _BYTES_ ALLOC: 3296
SUM _NUMBER_ OF _BYTES_ FREE: 3296
         LOW_COUNT_USED: 0
     CURRENT_COUNT_USED: 0
        HIGH_COUNT_USED: 1
LOW _NUMBER_ OF _BYTES_ USED: 0
CURRENT _NUMBER_ OF _BYTES_ USED: 0
HIGH _NUMBER_ OF _BYTES_ USED: 32
1 row in set (0.00 sec)
# memory_summary_by_host_by_event_name表
root@localhost : performance _schema 11:54:36> select * from memory_ summary _by_ host _by_ event _name where COUNT_ ALLOC!=0 limit 1\G
*************************** 1. row ***************************
                   HOST: NULL
             EVENT_NAME: memory/innodb/fil0fil
            COUNT_ALLOC: 158
......
1 row in set (0.00 sec)
# memory_summary_by_thread_by_event_name表
root@localhost : performance _schema 11:55:11> select * from memory_ summary _by_ thread _by_ event _name where COUNT_ ALLOC!=0 limit 1\G
*************************** 1. row ***************************
              THREAD_ID: 37
             EVENT_NAME: memory/innodb/fil0fil
            COUNT_ALLOC: 193
......
1 row in set (0.00 sec)
# memory_summary_by_user_by_event_name表
root@localhost : performance _schema 11:55:36> select * from memory_ summary _by_ user _by_ event _name where COUNT_ ALLOC!=0 limit 1\G
*************************** 1. row ***************************
                   USER: NULL
             EVENT_NAME: memory/innodb/fil0fil
            COUNT_ALLOC: 216
......
1 row in set (0.00 sec)
# memory_summary_global_by_event_name表
root@localhost : performance _schema 11:56:02> select * from memory_ summary _global_ by _event_ name where COUNT_ALLOC!=0 limit 1\G
*************************** 1. row ***************************
             EVENT_NAME: memory/performance_schema/mutex_instances
            COUNT_ALLOC: 1
......
1 row in set (0.00 sec)


從上面表中的示例記錄資訊中,我們可以看到,同樣與等待事件類似,按照使用者、主機、使用者+主機、執行緒等緯度進行分組與統計的列,分組列與等待事件類似,這裡不再贅述,但對於記憶體統計事件,統計列與其他幾種事件統計列不同(因為記憶體事件不統計時間開銷,所以與其他幾種事件型別相比無相同統計列),如下:

每個記憶體統計表都有如下統計列:  

* COUNT_ALLOC,COUNT_FREE:對記憶體分配和釋放記憶體函式的呼叫總次數 

* SUM_NUMBER_OF_BYTES_ALLOC,SUM_NUMBER_OF_BYTES_FREE:已分配和已釋放的記憶體塊的總位元組大小 

* CURRENT_COUNT_USED:這是一個便捷列,等於COUNT_ALLOC - COUNT_FREE 

* CURRENT_NUMBER_OF_BYTES_USED:當前已分配的記憶體塊但未釋放的統計大小。這是一個便捷列,等於SUM_NUMBER_OF_BYTES_ALLOC - SUM_NUMBER_OF_BYTES_FREE 

* LOW_COUNT_USED,HIGH_COUNT_USED:對應CURRENT_COUNT_USED列的低和高水位標記 

* LOW_NUMBER_OF_BYTES_USED,HIGH_NUMBER_OF_BYTES_USED:對應CURRENT_NUMBER_OF_BYTES_USED列的低和高水位標記

記憶體統計表允許使用TRUNCATE TABLE語句。使用truncate語句時有如下行為: 

通常,truncate操作會重置統計資訊的基準資料(即清空之前的資料),但不會修改當前server的記憶體分配等狀態。也就是說,truncate記憶體統計表不會釋放已分配記憶體 

  將COUNT_ALLOC和COUNT_FREE列重置,並重新開始計數(等於記憶體統計資訊以重置後的數值作為基準資料)

SUM_NUMBER_OF_BYTES_ALLOC和SUM_NUMBER_OF_BYTES_FREE列重置與COUNT_ALLOC和COUNT_FREE列重置類似 

LOW_COUNT_USED和HIGH_COUNT_USED將重置為CURRENT_COUNT_USED列值 

 LOW_NUMBER_OF_BYTES_USED和HIGH_NUMBER_OF_BYTES_USED將重置為CURRENT_NUMBER_OF_BYTES_USED列值 

此外,按照帳戶,主機,使用者或執行緒分類統計的記憶體統計表或memory_summary_global_by_event_name表,如果在對其依賴的accounts、hosts、users表執行truncate時,會隱式對這些記憶體統計表執行truncate語句

 

關於記憶體事件的行為監控設定與注意事項

記憶體行為監控設定:

* 記憶體instruments在setup_instruments表中具有memory/code_area/instrument_name格式的名稱。但預設情況下大多數instruments都被禁用了,預設只開啟了memory/performance_schema/*開頭的instruments

* 以字首memory/performance_schema命名的instruments可以收集performance_schema自身消耗的內部快取區大小等資訊。memory/performance_schema/* instruments預設啟用,無法在啟動時或執行時關閉。performance_schema自身相關的記憶體統計資訊只儲存在memory_summary_global_by_event_name表中,不會儲存在按照帳戶,主機,使用者或執行緒分類聚合的記憶體統計表中

* 對於memory instruments,setup_instruments表中的TIMED列無效,因為記憶體操作不支援時間統計

* 注意:如果在server啟動之後再修改memory instruments,可能會導致由於丟失之前的分配運算元據而導致在釋放之後記憶體統計資訊出現負值,所以不建議在執行時反覆開關memory instruments,如果有記憶體事件統計需要,建議在server啟動之前就在my.cnf中配置好需要統計的事件採集

當server中的某執行緒執行了記憶體分配操作時,按照如下規則進行檢測與聚合:

* 如果該執行緒在threads表中沒有開啟採集功能或者說在setup_instruments中對應的instruments沒有開啟,則該執行緒分配的記憶體塊不會被監控

* 如果threads表中該執行緒的採集功能和setup_instruments表中相應的memory instruments都啟用了,則該執行緒分配的記憶體塊會被監控

對於記憶體塊的釋放,按照如下規則進行檢測與聚合:

* 如果一個執行緒開啟了採集功能,但是記憶體相關的instruments沒有啟用,則該記憶體釋放操作不會被監控到,統計資料也不會發生改變

* 如果一個執行緒沒有開啟採集功能,但是記憶體相關的instruments啟用了,則該記憶體釋放的操作會被監控到,統計資料會發生改變,這也是前面提到的為啥反覆在執行時修改memory instruments可能導致統計資料為負數的原因

對於每個執行緒的統計資訊,適用以下規則。

當一個可被監控的記憶體塊N被分配時,performance_schema會對記憶體統計表中的如下列進行更新:  

* COUNT_ALLOC:增加1 

* CURRENT_COUNT_USED:增加1 

* HIGH_COUNT_USED:如果CURRENT_COUNT_USED增加1是一個新的最高值,則該欄位值相應增加 

* SUM_NUMBER_OF_BYTES_ALLOC:增加N 

* CURRENT_NUMBER_OF_BYTES_USED:增加N 

* HIGH_NUMBER_OF_BYTES_USED:如果CURRENT_NUMBER_OF_BYTES_USED增加N之後是一個新的最高值,則該欄位值相應增加

當一個可被監控的記憶體塊N被釋放時,performance_schema會對統計表中的如下列進行更新: 
* COUNT_FREE:增加1 
* CURRENT_COUNT_USED:減少1 
* LOW_COUNT_USED:如果CURRENT_COUNT_USED減少1之後是一個新的最低值,則該欄位相應減少 
* SUM_NUMBER_OF_BYTES_FREE:增加N 
* CURRENT_NUMBER_OF_BYTES_USED:減少N 
* LOW_NUMBER_OF_BYTES_USED:如果CURRENT_NUMBER_OF_BYTES_USED減少N之後是一個新的最低值,則該欄位相應減少

對於較高階別的聚合(全域性,按帳戶,按使用者,按主機)統計表中,低水位和高水位適用於如下規則 :
* LOW_COUNT_USED和LOW_NUMBER_OF_BYTES_USED是較低的低水位估算值。performance_schema輸出的低水位值可以保證統計表中的記憶體分配次數和記憶體小於或等於當前server中真實的記憶體分配值 
* HIGH_COUNT_USED和HIGH_NUMBER_OF_BYTES_USED是較高的高水位估算值。performance_schema輸出的低水位值可以保證統計表中的記憶體分配次數和記憶體大於或等於當前server中真實的記憶體分配值

對於記憶體統計表中的低水位估算值,在memory_summary_global_by_event_name表中如果記憶體所有權線上程之間傳輸,則該估算值可能為負數


|   溫馨提示

效能事件統計表中的資料條目是不能刪除的,只能把相應統計欄位清零;

效能事件統計表中的某個instruments是否執行統計,依賴於在setup_instruments表中的配置項是否開啟;

效能事件統計表在setup_consumers表中只受控於"global_instrumentation"配置項,也就是說一旦"global_instrumentation"配置項關閉,所有的統計表的統計條目都不執行統計(統計列值為0);

記憶體事件在setup_consumers表中沒有獨立的配置項,且memory/performance_schema/* instruments預設啟用,無法在啟動時或執行時關閉。performance_schema相關的記憶體統計資訊只儲存在memory_summary_global_by_event_name表中,不會儲存在按照帳戶,主機,使用者或執行緒分類聚合的記憶體統計表中。


下期將為大家分享 《資料庫物件事件統計與屬性統計 | performance_schema全方位介紹》 ,謝謝你的閱讀,我們下期不見不散!




1.利用等待事件排查MySQL效能問題

通常,在生產伺服器上線之前, 我們會對資料庫伺服器的硬體進行IO基準測試,對資料庫進行增刪改查的基準測試,建立基線參考資料,以便日後的伺服器擴容或架構升級提供資料支撐。在基準測試規劃時,我們通常需要選擇一款基準測試軟體(IO基準測試通常選擇fio和iozone,MySQL資料庫基準測試通常選擇sysbench、tpcc-mysql、workbench等),在使用這些基準測試軟體對伺服器壓測到一個極限值時,我們認為所得資料就是被測試伺服器的最高效能。但這還不夠,測試效能無法繼續提升的原因還可能是因為你的伺服器在BIOS設定、硬體搭配、作業系統引數、檔案系統策略、資料庫配置引數等方面不夠優化。所以我們還需要藉助一些效能排查手段來找出效能瓶頸所在,以使得我們對資料庫伺服器一旦上線之後可能的瓶頸點心中有數。以下我們以sysbench基準測試工具壓測MySQL資料庫為例,介紹如何使用performance_schema的等待事件來排查資料庫效能瓶頸所在。 

首先,使用performance_schema配置表啟用等待事件的採集與記錄



# 啟用所有的等待事件的instruments


admin@localhost : performance_schema  11:47:46 > use performance_schema
Database changed
# 修改setup_instruments 表的enabled和timed欄位為yes,表示啟用對應的instruments
admin@localhost : performance_schema  11:48:05 > update setup_instruments set enabled= 'yes' ,timed= 'yes'  where name like  'wait/%' ;
Query OK,  269  rows affected ( . 00  sec)
Rows  matched:   323    Changed:   269    Warnings:  
# 檢視修改結果,enabled和timed欄位為YES即表示當前instruments已經啟用(但此時採集器並不會立即採集事件資料,需要儲存這些等待事件的表--consumers,啟用之後才會開始採集)
admin@localhost : performance_schema  11:49:40 > select * from setup_instruments where name like  'wait/%' ;
+--------------------------------------------------------------------+---------+-------+
| NAME                                                              |  ENABLED  | TIMED |
+--------------------------------------------------------------------+---------+-------+
| wait/synch/mutex/sql/TC_LOG_MMAP::LOCK_tc                          |  YES     | YES  |
| wait/synch/mutex/sql/LOCK_des_key_file                            |  YES     | YES  |
............
| wait/io/socket/sql/server_tcpip_socket                            |  YES     | YES  |
| wait/io/socket/sql/server_unix_socket                              |  YES     | YES  |
| wait/io/socket/sql/client_connection                              |  YES     | YES  |
| wait/lock/metadata/sql/mdl                                        |  YES     | YES  |
+--------------------------------------------------------------------+---------+-------+
323  rows  in  set ( . 01  sec)
# 啟用等待事件的consumers
admin@localhost : performance_schema  11:48:21 > update setup_consumers set enabled= 'yes'  where name like  '%wait%' ;
Query OK,  3  rows affected ( . 00  sec)
Rows  matched:   3    Changed:   3    Warnings:  
admin@localhost : performance_schema  11:49:20 > select * from setup_consumers where name like  '%wait%' ;
+---------------------------+---------+
| NAME                      |  ENABLED  |
+---------------------------+---------+
|
 events_waits_current       | YES    |
| events_waits_history      |  YES     |
|
 events_waits_history_long  | YES    |
+---------------------------+---------+
3  rows  in  set ( . 00  sec)


然後,使用sysbench對資料庫執行加壓,並逐漸增加併發執行緒數,直到tps、qps不再隨著執行緒數的增加而增加為止。


sysbench --

test

=oltp --db-driver=mysql --mysql-table-engine=innodb --mysql-host=10.10.10.10 --mysql-port=3306 --mysql-db=sbtest --mysql-user=

'qbench'

 --mysql-password=

'qbench'

 --

test

=/usr/share/doc/sysbench/tests/db/oltp.lua --oltp-table-size=5000000 --oltp-tables-count=8 --num-threads=16 --max-time=1800 --max-requests=0 --report-interval=1 run

............
[ 111s] threads: 16, tps: 52.99, reads/s: 668.93, writes/s: 171.98, response time: 629.52ms (95%)
[ 112s] threads: 16, tps: 42.00, reads/s: 650.93, writes/s: 202.98, response time: 688.46ms (95%)
............


從sysbench的輸出結果中,我們可以看到在16個併發執行緒oltp壓力下,tps只能跑到100不到,且延遲在600ms+,說明存在嚴重的效能瓶頸(或者在MySQL內部發生了嚴重的互斥等待,或者硬體裝置嚴重的效能不足),現在,我們先使用作業系統命令檢視硬體負載情況。



# top命令檢視到CPU資源絕大部分都消耗在了%wa上,說明IO裝置效能出現嚴重不足


[root@localhost ~] # top
top -  18 : 59 : 03  up   7 : 02 ,   3  users,  load average:  4.28 5.82 4.22
Tasks:  186  total,   1  running,  185  sleeping,    stopped,    zombie
Cpu   :   4.1 %us,   8.5 %sy,   . %ni,  11.9 %id,  75.4 %wa,   . %hi,   . %si,   . %st
Cpu1  :   4.0 %us,  13.1 %sy,   . %ni,  17.5 %id,  65.0 %wa,   . %hi,   . 3 %si,   . %st
Cpu2  :   9.4 %us,  32.1 %sy,   . %ni,   2.3 %id,  55.5 %wa,   . %hi,   . 7 %si,   . %st
Cpu3  :   3.0 %us,   5.3 %sy,   . %ni,  31.0 %id,  60.0 %wa,   . %hi,   . 7 %si,   . %st
Mem:   8053664 k total,   1684236 k used,   6369428 k free,     87868 k buffers
Swap:   2031612 k total,        0k used,   2031612 k free,   150680 k cached
# iostat命令檢視磁碟負載,通過%util列可以看到,磁碟處於100%滿負載狀態
avg-cpu:  %user  %nice %system %iowait  %steal  %idle
       1.77      . 00      2.28    95.70      . 00      . 25
Device:        rrqm/ s   wrqm/ s     r/ s     w/ s   rsec/ s   wsec/ s  avgrq-sz avgqu-sz  await  svctm  %util
dm- 2                . 00      . 00    277.00    160.00    8864.00    2774.00      26.63      47.84    112.98    2.29   100.10
avg-cpu:  %user  %nice %system %iowait  %steal  %idle
       5.05      . 00    11.62    64.14      . 00    19.19
Device:        rrqm/ s   wrqm/ s     r/ s     w/ s   rsec/ s   wsec/ s  avgrq-sz avgqu-sz  await  svctm  %util
dm- 2                . 00      . 00    267.00    244.00    8544.00    4643.00      25.81      28.20    40.29    1.96   100.00


通過查詢系統負載,一眼就可以看出來是由於磁碟效能嚴重不足導致的,但是,在資料庫內部的事件資訊是如何體現的呢(注意:如果你沒有足夠的performance_schema使用經驗,此時是絕好的學習積累的機會,不要錯過,也許哪一天作業系統負載並不能看出來端倪的時候,這些事件資訊能幫上大忙)



# 為了方便查詢等待事件統計,我們可以先建立一個檢視,用於實時統計當前等待事件(非歷史資料)


admin@localhost : performance_schema  12:14:14 > create view sys.test_waits as select sum(TIMER_WAIT) as TIMER_WAIT,sum(NUMBER_OF_BYTES) as NUMBER_OF_BYTES, EVENT_NAME,OPERATION from events_waits_current where EVENT_NAME!= 'idle'  group by EVENT_NAME,OPERATION;
Query OK,   rows affected ( . 04  sec)
# 使用前面建立的檢視進行查詢,對這個檢視查詢結果進行降序排序查詢。從下面的查詢結果中,我們可以看到時間開銷排名前5的有4個都是與IO相關的等待,剩下1個是binlog相關的互斥等待
admin@localhost : performance_schema  12:30:38 > select sys.format_time(TIMER_WAIT),sys.format_bytes(NUMBER_OF_BYTES),EVENT_NAME,OPERATION from sys.test_waits where sys.format_time(TIMER_WAIT)  not  regexp  'ns|us'  order by TIMER_WAIT desc;
+-----------------------------+-----------------------------------+------------------------------------------------+------------+
| sys.format_time(TIMER_WAIT) |  sys.format_bytes(NUMBER_OF_BYTES)  | EVENT_NAME                                    |  OPERATION   |
+-----------------------------+-----------------------------------+------------------------------------------------+------------+
|
  16.60  s                     | 224.00 KiB                        |  wait/io/file/innodb/innodb_data_file           | read      |
| 16.05 s                    |   553  bytes                         | wait/io/table/sql/handler                      |  fetch       |
|
  1.96  s                       | NULL                              |  wait/io/file/sql/binlog                         | sync      |
| 1.96 s                      |  NULL                               | wait/synch/cond/sql/MYSQL_BIN_LOG::update_cond |  timed_wait  |
|
  1.85  s                       | 1.34 KiB                          |  wait/io/file/sql/binlog                         | write      |
| 56.66 ms                    |  NULL                               | wait/io/file/innodb/innodb_log_file            |  sync       |
+-----------------------------+-----------------------------------+------------------------------------------------+------------+
6 rows  in  set (0.01 sec)
# 當然,你也可以直接查詢events_waits_current表(返回資料行數可能比較多,且查詢結果並沒有做分組聚合,是逐行的事件記錄資料)
admin@localhost : performance_schema 11:59:25> select THREAD_ID,EVENT_NAME,sys.format_time(TIMER_WAIT),INDEX_NAME,NESTING_EVENT_TYPE,OPERATION,NUMBER_OF_BYTES from events_waits_current where EVENT_NAME!='idle' order by TIMER_WAIT desc;
+-----------+------------------------------------------------+-----------------------------+------------+--------------------+------------+-----------------+
|
 THREAD_ID  | EVENT_NAME                                    |  sys.format_time(TIMER_WAIT)  | INDEX_NAME |  NESTING_EVENT_TYPE  | OPERATION  |  NUMBER_OF_BYTES  |
+-----------+------------------------------------------------+-----------------------------+------------+--------------------+------------+-----------------+
|
       115   | wait/io/table/sql/handler                      |   169.48  ms                   | PRIMARY    |  STATEMENT           | fetch      |                39   |
|
       115   | wait/io/file/innodb/innodb_data_file          |   169.48  ms                   | NULL      |  WAIT               | read      |            16384   |
|
       101   | wait/io/table/sql/handler                      |   93.76  ms                     | PRIMARY    |  STATEMENT           | fetch      |                39   |
|
       101   | wait/io/file/innodb/innodb_data_file          |   93.76  ms                     | NULL      |  WAIT               | read      |            16384   |
|
       111   | wait/io/file/innodb/innodb_data_file          |   73.08  ms                     | NULL      |  STATEMENT           | read      |            16384   |
|
       103   | wait/io/file/innodb/innodb_data_file          |   63.13  ms                     | NULL      |  STATEMENT           | read      |            16384   |
|
       106   | wait/io/file/innodb/innodb_data_file          |   53.24  ms                     | NULL      |  STATEMENT           | read      |            16384   |
|
       113   | wait/io/table/sql/handler                      |   51.90  ms                     | PRIMARY    |  STATEMENT           | fetch      |                39   |
|
       113   | wait/io/file/innodb/innodb_data_file          |   51.90  ms                     | NULL      |  WAIT               | read      |            16384   |
|
         49   | wait/synch/cond/sql/MYSQL_BIN_LOG::update_cond |   27.48  ms                     | NULL      |  STATEMENT           | timed_wait |             NULL  |
............
57 rows  in  set (0.00 sec)


從上述等待事件的查詢結果中,我們可以非常清晰地看到,事務大多數的延遲時間花在了等待IO上(主要是undo log、redo log,獨立表空間檔案,binlog的fetch和read系統呼叫),說明IO裝置可能出現了嚴重的效能瓶頸,這裡與作業系統命令檢視到的磁碟效能嚴重不足相對應。

結論:通過以上測試資料表明,MySQL的效能嚴重低下的原因是因為磁碟效能嚴重不足成為了瓶頸(一般情況下,4個core的cpu在記憶體和磁碟不構成瓶頸的情況下可以達到800+ tps才可能會構成瓶頸)

針對IO效能不足,建議優化策略:

  • 更換IO效能更好的裝置

  • 新增2個獨立的相同裝置,把MySQL 中的redo log、binlog、其他data file分別放在3個獨立的IO裝置上,以便資料庫中的隨機IO和順序IO不會因為相互爭搶資源而導致IO等待

PS:

當然,你也許會說,我們在這個案例裡故意使用一臺配置很差的伺服器。是的沒錯。但我們可以去思考一個問題:performance_schema到底能夠對我們使用MySQL提供多大幫助呢?對於目前來講,網際網路上並不能找到太多靠譜的performance_schema使用經驗,需要我們不斷地去挖掘。我們建議有條件的同行可以準備兩臺測試伺服器(一臺低配置,一臺高配置伺服器),通過對比測試資料你就能得出performance_schema的使用經驗了,正所謂沒有對比就沒有傷害。


2.鎖問題排查

2.1 找出誰持有全域性讀鎖

全域性讀鎖通常是由flush table with read lock;這類語句新增,這類語句通常是在各種備份工具為了拿到一致性備份時使用,另外,在具有主從複製架構的環境中做主備切換時也常常使用,除了這兩種情況之外,還有一種情況也是最難排查的一種情況,那就是線上系統許可權約束不規範的時候,各種人員使用的資料庫帳號都具有RELOAD許可權時,都可以對資料庫加全域性讀鎖。 

在MySQL 5.7之前的版本,要排查誰持有全域性讀鎖通常在資料庫層面是很難直接查詢到有用資料(innodb_locks表也只能記錄innodb層的鎖資訊,而全域性讀鎖是server層的鎖,所以也無法查詢到),從MySQL 5.7開始提供表performance_schema.metadata_locks表記錄一些Server層的鎖資訊(包括全域性讀鎖和MDL鎖等),下面我們通過一個示例來演示然後使用performance_schema來找出誰持有全域性讀鎖。

首先,開啟第一個會話,執行全域性讀鎖。


# 執行加鎖語句

root@localhost : sbtest  12 : 28 : 30 flush   table  with  read  lock;
Query OK,   rows affected ( 0.00  sec)
# 查詢以下加鎖執行緒的process id,以便後續排查過程好對應
root@localhost : sbtest  12 : 31 : 48 select  connection_id();
+ -----------------+
| connection_id() |
+ -----------------+
4  |
+ -----------------+
1  row  in  set ( 0.00  sec)


現在,我們開啟第二個會話執行任意可能對資料造成修改的語句,我們就以update操作為例吧。


root@localhost : sbtest 12:42:36> use sbtest

Database changed
root@localhost : sbtest 12:42:38> select * from sbtest1 limit 1\G;
*************************** 1. row ***************************
id: 21
k: 2483476
c: 09279210219-37745839908-56185699327-79477158641-86711242956-61449540392-42622804506-61031512845-36718422840-11028803849
pad: 96813293060-05308009118-09223341195-19224109585-45598161848
1 row in set (0.00 sec)
ERROR: 
No query specified
root@localhost : sbtest 12:42:39> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 5 |
+-----------------+
1 row in set (0.00 sec)
root@localhost : sbtest 12:42:44> update sbtest1 set pad='xxx' where id=21;  操作被阻塞


現在,我們開啟第三個會話,開始使用一些手段進行排查。


root@localhost : (none) 

12:42:25

> select connection_id();

+-----------------+
| connection_id() |
+-----------------+
| 16 |
+-----------------+
1  row  in  set ( . 00  sec)
# 查詢processlist資訊,這裡只能看到processid為5的執行緒State為Waiting for global read lock,表示正在等待全域性讀鎖
root@localhost : (none)  12:43:11 > show processlist;
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+------------------------------------------+
| Id |  User  | Host |  db  | Command |  Time  | State |  Info  |
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+------------------------------------------+
|
  3   | qfsys |   192.168 . 2.168:41042   | NULL |  Binlog Dump  | 11457 |  Master has sent all binlog to slave; waiting  for  more updates  | NULL |
| 4 |  root  | localhost |  sbtest  | Sleep |   234   | |  NULL  |
|
  5   | root |  localhost  | sbtest |  Query  | 26 |  Waiting  for  global read lock  | update sbtest1 set pad='xxx' where id=21 |
| 16 |  root  | localhost |  NULL  | Query |     | starting |  show processlist  |
+----+-------+---------------------+--------+-------------+-------+---------------------------------------------------------------+------------------------------------------+
4 rows  in  set (0.00 sec)
# 繼續查詢information_schema.innodb_locks、innodb_lock_waits、innodb_trx表,發現三個表均為空
root@localhost : (none) 12:59:30> select * from information_schema.innodb_locks;
Empty set, 1 warning (0.00 sec)
root@localhost : (none) 12:59:40> select * from information_schema.innodb_lock_waits;
Empty set, 1 warning (0.00 sec)
root@localhost : (none) 12:59:43> select * from information_schema.innodb_trx\G
Empty set (0.00 sec)
# 再使用show engine innodb status;檢視一把(這裡只需要看TRANSACTION段落即可),仍然無任何有用的鎖資訊
root@localhost : (none) 12:59:48> show engine innodb status;
......
=====================================
2018-06-25 13:01:43 0x7fe55ded8700 INNODB MONITOR OUTPUT
=====================================
......
------------
TRANSACTIONS
------------
Trx id counter 2527502
Purge done  for  trx's n:o < 2527500 undo n:o < 0 state: running but idle
History list length 3
LIST OF TRANSACTIONS FOR EACH SESSION:
---TRANSACTION 422099353083504,  not  started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 422099353082592,  not  started
0 lock struct(s), heap size 1136, 0 row lock(s)
---TRANSACTION 422099353081680,  not  started
0 lock struct(s), heap size 1136, 0 row lock(s)
--------
FILE I/O
......


通過上面的常規手段查詢下來,無任何有用資訊,這個時候,有gdb除錯經驗的老鳥估計就要開始使用gdb,strace,pstack什麼的命令檢視MySQL 呼叫棧、執行緒資訊什麼的了,但這對於沒有C語言基礎的人來說,基本上是看天書,好在從MySQL 5.7版本開始,提供performance_schema.metadata_locks表,該表記錄了各種Server層的鎖資訊(包括全域性讀鎖和MDL鎖資訊),下面我們開啟第三個會話查詢該表試試看。



# 我們還可以通過performance_schema.metadata_locks表來排查誰持有全域性讀鎖,全域性讀鎖通常在該表記錄著同一個會話的OBJECT_TYPE為global和commit、LOCK_TYPE都為SHARED的兩把顯式鎖,如下


root@localhost : (none) 01:01:43> select * from performance _schema.metadata_ locks where OWNER _THREAD_ ID!=sys.ps _thread_ id(connection_id())\G;
*************************** 1. row ***************************
      OBJECT_TYPE: GLOBAL
    OBJECT_SCHEMA: NULL
      OBJECT_NAME: NULL
OBJECT _INSTANCE_ BEGIN: 140621322913984
        LOCK_TYPE: SHARED  # 共享鎖
    LOCK_DURATION: EXPLICIT  # 顯式
      LOCK_STATUS: GRANTED  # 已授予
           SOURCE: lock.cc:1110
  OWNER _THREAD_ ID: 94 # 持有鎖的內部執行緒ID為94
   OWNER _EVENT_ ID: 16  
*************************** 2. row ***************************
      OBJECT_TYPE: COMMIT
    OBJECT_SCHEMA: NULL
      OBJECT_NAME: NULL
OBJECT _INSTANCE_ BEGIN: 140621322926064
        LOCK_TYPE: SHARED # 共享鎖
    LOCK_DURATION: EXPLICIT  # 顯式
      LOCK_STATUS: GRANTED # 已授予
           SOURCE: lock.cc:1194
  OWNER _THREAD_ ID: 94  # 持有鎖的內部執行緒ID為94
   OWNER _EVENT_ ID: 16  
*************************** 3. row ***************************
      OBJECT_TYPE: GLOBAL
    OBJECT_SCHEMA: NULL
      OBJECT_NAME: NULL
OBJECT _INSTANCE_ BEGIN: 140621391527216
        LOCK _TYPE: INTENTION_ EXCLUSIVE  # 意向排它鎖
    LOCK_DURATION: STATEMENT  # 語句
      LOCK_STATUS: PENDING  # 狀態為pending,表示正在等待被授予
           SOURCE: sql_base.cc:3190
  OWNER _THREAD_ ID: 95  # 被阻塞的內部執行緒ID為95
   OWNER _EVENT_ ID: 38 
3 rows in set (0.00 sec)
# 檢視process id為4,5 各自對應的內部執行緒ID是多少
root@localhost : (none) 01:33:36> select sys.ps _thread_ id(4);
+---------------------+
| sys.ps _thread_ id(4) |
+---------------------+
| 94 |    # process id=4的執行緒對應的內部執行緒ID正好為94,說明就是process id=4的執行緒持有了全域性讀鎖
+---------------------+
1 row in set (0.00 sec)
root@localhost : (none) 01:34:10> select sys.ps _thread_ id(5);
+---------------------+
| sys.ps _thread_ id(5) |
+---------------------+
| 95 |   # proces id=5的執行緒對應的內部執行緒正好是95,說明在等待全域性讀鎖的就是process id=5的執行緒
+---------------------+
1 row in set (0.00 sec)


如果是生產環境,綜合上述資訊,通過show processlist資訊中對應的process id=4的行記錄中找到user、host、db資訊,大致判斷一下是屬於什麼業務用途,找相關人員詢問清楚,該殺掉就殺掉,順便討論下今後如何避免這個問題。

2.2 找出誰持有MDL鎖

我們可能經常會發現執行語句時被阻塞等待MDL鎖,例如:使用show processlist;語句檢視執行緒資訊時可能會發現State列為"Waiting for table metadata lock",碰到這種情況我們應該如何去排查是誰持有了MDL鎖沒有釋放呢,下面我們嘗試著進行MDL鎖等待場景模擬(mdl鎖記錄對應的instruments為wait/lock/metadata/sql/mdl,預設未啟用,對應的consumers為performance_schema.metadata_locks,在setup_consumers只受全域性配置項global_instrumentation控制,預設啟用)。

首先,開啟兩個會話,分別執行如下語句。



# 會話1,顯式開啟一個事務,並執行一個update語句更新sbtest1表不提交


root@localhost : sbtest  12 : 26 : 25 use  sbtest
Database changed
root@localhost : sbtest  12 : 26 : 30 > begin;
Query OK,   rows affected ( . 00  sec)
root@localhost : sbtest  12 : 26 : 32 > update sbtest1 set pad= 'yyy'  where id= 1 ;
Query OK,  1  row affected ( . 00  sec)
Rows matched:  1   Changed:  1   Warnings: 
# 會話2,對sbtest1表執行DDL語句新增一個普通索引
root@localhost : sbtest  12 : 42 : 50 use  sbtest
Database changed
root@localhost : sbtest  12 : 42 : 56 > alter table sbtest1 add  index  i_c(c);   # 被阻塞


此時,我們另外開啟一個會話3,使用show processlist語句查詢執行緒資訊,可以發現update語句正在等待MDL鎖(Waiting for table metadata lock)。


root@localhost : (none) 

12:37:49

> show processlist;

+----+------+-----------+--------+---------+------+---------------------------------+--------------------------------------+
| Id |  User  | Host      |  db     | Command |  Time  | State                          |  Info                                 |
+----+------+-----------+--------+---------+------+---------------------------------+--------------------------------------+
|
  92   | root |  localhost  | sbtest |  Query   |  121 |  Waiting  for  table metadata lock  | alter table sbtest1 add index i_c(c) |
| 93 |  root  | localhost |  NULL   | Query  |        | starting                        |  show processlist                     |
|
  94   | root |  localhost  | sbtest |  Sleep   | 1078 |                                  | NULL                                |
+----+------+-----------+--------+---------+------+---------------------------------+--------------------------------------+
3  rows  in  set ( . 00  sec)


在MySQL 5.7版本之前,我們不能從資料庫層面很直觀地去查詢誰持有MDL鎖資訊(當然你可以說你會gdb之類的工具來檢視,但這類工具的使用需要具有一定c程式語言基礎),現在,我們可以通過查詢performance_schema.metadata_locks表得知MDL鎖資訊,發現有5行MDL鎖記錄,其中, 第一行為sbtest.sbtest1表的SHARED_WRITE鎖,處於GRANTED狀態,為136執行緒持有(對應process id為94),其他後續4行中,有sbtest.sbtest1表的SHARED_UPGRADABLE、EXCLUSIVE鎖,其中SHARED_UPGRADABLE處於GRANTED狀態,EXCLUSIVE處於PENDING狀態,為134執行緒持有(對應process id為92)。說明134執行緒在等待MDL鎖。


root@localhost : (none) 01:23:05> select * from performance

_schema.metadata_

locks where OWNER

_THREAD_

ID!=sys.ps

_thread_

id(connection_id())\G;

*************************** 1. row ***************************
      OBJECT_TYPE: TABLE
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: sbtest1
OBJECT _INSTANCE_ BEGIN: 139886013386816
        LOCK _TYPE: SHARED_ WRITE
    LOCK_DURATION: TRANSACTION
      LOCK_STATUS: GRANTED
          SOURCE: sql_parse.cc:5996
  OWNER _THREAD_ ID: 136
  OWNER _EVENT_ ID: 721
*************************** 2. row ***************************
      OBJECT_TYPE: GLOBAL
    OBJECT_SCHEMA: NULL
      OBJECT_NAME: NULL
OBJECT _INSTANCE_ BEGIN: 139886348911600
        LOCK _TYPE: INTENTION_ EXCLUSIVE
    LOCK_DURATION: STATEMENT
      LOCK_STATUS: GRANTED
          SOURCE: sql_base.cc:5497
  OWNER _THREAD_ ID: 134
  OWNER _EVENT_ ID: 4667
*************************** 3. row ***************************
      OBJECT_TYPE: SCHEMA
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: NULL
OBJECT _INSTANCE_ BEGIN: 139886346748096
        LOCK _TYPE: INTENTION_ EXCLUSIVE
    LOCK_DURATION: TRANSACTION
      LOCK_STATUS: GRANTED
          SOURCE: sql_base.cc:5482
  OWNER _THREAD_ ID: 134
  OWNER _EVENT_ ID: 4667
*************************** 4. row ***************************
      OBJECT_TYPE: TABLE
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: sbtest1
OBJECT _INSTANCE_ BEGIN: 139886346749984
        LOCK _TYPE: SHARED_ UPGRADABLE
    LOCK_DURATION: TRANSACTION
      LOCK_STATUS: GRANTED
          SOURCE: sql_parse.cc:5996
  OWNER _THREAD_ ID: 134
  OWNER _EVENT_ ID: 4669
*************************** 5. row ***************************
      OBJECT_TYPE: TABLE
    OBJECT_SCHEMA: sbtest
      OBJECT_NAME: sbtest1
OBJECT _INSTANCE_ BEGIN: 139886348913168
        LOCK_TYPE: EXCLUSIVE
    LOCK_DURATION: TRANSACTION
      LOCK_STATUS: PENDING
          SOURCE: mdl.cc:3891
  OWNER _THREAD_ ID: 134
  OWNER _EVENT_ ID: 4748
5 rows in set (0.00 sec)


通過上述資料,我們知道了是哪個執行緒持有了MDL鎖,通過show processlist語句的查詢結果可以看到process id為94的執行緒已經長時間處於sleep狀態,但是我們在這裡並不能看到這個執行緒執行了什麼語句,我們可能需要查詢一下information_schema.innodb_trx表,確認一下該執行緒是否存在著一個沒有提交的事務。如下,通過查詢該表發現process id為94(trx_mysql_thread_id=94)的執行緒確實有一個未提交的事務,但並沒有太多的有用資訊,除了一個事務開始時間和process id(trx_started: 2018-01-14 01:19:25, trx_mysql_thread_id: 94)


root@localhost : (none) 01:32:17> select * from information

_schema.innodb_

trx\G;

*************************** 1. row ***************************
                trx_id: 2452892
            trx_state: RUNNING
          trx_started: 2018-01-14 01:19:25
trx _requested_ lock_id: NULL
      trx _wait_ started: NULL
            trx_weight: 3
  trx _mysql_ thread_id: 94
......
1 row in set (0.00 sec)


此時,從我們掌握的所有資料資訊來看,雖然知道了是136執行緒的事務沒有提交導致的134執行緒發生MDL鎖等待,但是我們並不知道136執行緒正在做什麼事情。我們當然可以kill掉136執行緒讓134執行緒繼續往下執行,但是我們不知道136執行緒在執行什麼語句,就無法找到相關的開發人員進行優化,下次我們還可能再次碰到類似的問題,所以,我們還可以藉助performance_schema.events_statements_current表來查詢某個執行緒正在執行或者說最後一次執行完成的語句事件資訊(這裡資訊並不一定可靠,因為該表中對於每個執行緒只能記錄當前正在執行和最近一次執行完成的語句事件資訊,一旦這個執行緒執行新的語句,資訊就會被覆蓋),如下:


root@localhost : (none) 01:47:53> select * from performance

_schema.events_

statements

_current where thread_

id=136\G;

*************************** 1. row ***************************
          THREAD_ID: 136
          EVENT_ID: 715
      END _EVENT_ ID: 887
        EVENT_NAME: statement/sql/update
            SOURCE: socket_connection.cc:101
......
          SQL_TEXT: update sbtest1 set pad='yyy' where id=1
            DIGEST: 69f516aa8eaa67fd6e7bfd3352de5d58
        DIGEST_TEXT: UPDATE  `sbtest1`  SET  `pad`  = ? WHERE  `id`  = ? 
    CURRENT_SCHEMA: sbtest
......
      MESSAGE_TEXT: Rows matched: 1  Changed: 1  Warnings: 0
......
1 row in set (0.00 sec)


從performance_schema.events_statements_current 表的查詢資訊中,通過SQL_TEXT欄位我們可以清晰地看到該執行緒正在執行的SQL語句是什麼。如果是生產環境,現在,你可以去找相關的開發人員交涉,下次碰到類似的語句必須及時提交,避免下次再發生類似的問題。

3. 檢視最近的SQL執行資訊


3.1 檢視最近的top sql


使用performance_schema中的語句當前事件記錄表和語句事件歷史記錄表可以查詢資料庫中最近執行的一些SQL語句,以及語句相關的資訊,這裡我們以events_statements_history表為例,查詢結果按照語句完成時間倒序排序,如下:



root@localhost : performance

_schema 04:33:33> select THREAD_

ID,EVENT

_NAME,SOURCE,sys.format_

time(TIMER

_WAIT),sys.format_

time(LOCK

_TIME),SQL_

TEXT,CURRENT

_SCHEMA,MESSAGE_

TEXT,ROWS

_AFFECTED,ROWS_

SENT,ROWS

_EXAMINED from events_

statements

_history where CURRENT_

SCHEMA!='performance

_schema' order by TIMER_

WAIT desc limit 10\G;

*************************** 1. row ***************************
              THREAD_ID: 114
             EVENT_NAME: statement/sql/update
                 SOURCE: socket_connection.cc:101
sys.format _time(TIMER_ WAIT): 24.93 m
sys.format _time(LOCK_ TIME): 24.93 m
               SQL_TEXT: update sbtest1 set pad='xxx' where id=1
         CURRENT_SCHEMA: sbtest
           MESSAGE_TEXT: Rows matched: 0 Changed: 0 Warnings: 0
          ROWS_AFFECTED: 0
              ROWS_SENT: 0
          ROWS_EXAMINED: 0
*************************** 2. row ***************************
              THREAD_ID: 114
             EVENT_NAME: statement/sql/update
                 SOURCE: socket_connection.cc:101
sys.format _time(TIMER_ WAIT): 7.84 m
sys.format _time(LOCK_ TIME): 7.84 m
               SQL_TEXT: update sbtest1 set pad='xxx' where id=1
         CURRENT_SCHEMA: sbtest
           MESSAGE_TEXT: Rows matched: 0 Changed: 0 Warnings: 0
          ROWS_AFFECTED: 0
              ROWS_SENT: 0
          ROWS_EXAMINED: 0
......
10 rows in set (0.00 sec)


按照我們通常優化慢SQL的原則,優先優化執行次數最多的,然後是執行時間最長的,以上的查詢結果並不是我們通常說的top sql,我們可以使用events_statements_summary_by_digest表來查詢經過統計之後的top sql



root@localhost : performance

_schema 05:04:41> select SCHEMA_

NAME,DIGEST

_TEXT,COUNT_

STAR,sys.format

_time(SUM_

TIMER

_WAIT) as sum_

time,sys.format

_time(MIN_

TIMER

_WAIT) as min_

time,sys.format

_time(AVG_

TIMER

_WAIT) as avg_

time,sys.format

_time(MAX_

TIMER

_WAIT) as max_

time,sys.format

_time(SUM_

LOCK

_TIME) as sum_

lock

_time,SUM_

ROWS

_AFFECTED,SUM_

ROWS

_SENT,SUM_

ROWS

_EXAMINED from events_

statements

_summary_

by

_digest where SCHEMA_

NAME is not null order by COUNT_STAR desc limit 10\G

*************************** 1. row ***************************
  SCHEMA_NAME: sbtest
  DIGEST_TEXT: UPDATE  `sbtest1`  SET  `pad`  = ? WHERE  `id`  = ? 
   COUNT_STAR: 10
     sum_time: 2.19 h
     min_time: 216.90 us
     avg_time: 13.15 m
     max_time: 1.50 h
sum _lock_ time: 2.04 h
SUM _ROWS_ AFFECTED: 3
SUM _ROWS_ SENT: 0
SUM _ROWS_ EXAMINED: 4
*************************** 2. row ***************************
  SCHEMA_NAME: sbtest
  DIGEST_TEXT: SHOW WARNINGS 
   COUNT_STAR: 9
     sum_time: 397.62 us
     min_time: 16.50 us
     avg_time: 44.18 us
     max_time: 122.58 us
sum _lock_ time: 0 ps
SUM _ROWS_ AFFECTED: 0
SUM _ROWS_ SENT: 0
SUM _ROWS_ EXAMINED: 0
......
*************************** 5. row ***************************
  SCHEMA_NAME: sbtest
  DIGEST_TEXT: SELECT * FROM  `sbtest1`  LIMIT ? 
   COUNT_STAR: 5
     sum_time: 138.93 ms
     min_time: 145.77 us
     avg_time: 27.79 ms
     max_time: 112.29 ms
sum _lock_ time: 95.53 ms
SUM _ROWS_ AFFECTED: 0
SUM _ROWS_ SENT: 104
SUM _ROWS_ EXAMINED: 104
......
10 rows in set (0.00 sec)


PS: events_statements_summary_by_digest 表中記錄的SQL語句文字並不完整,預設情況下只擷取了1024個位元組,且也是使用這1024個位元組的SQL文字進行hash計算,把hashcode相同的累計計算在一起,performance_schema提供的資料只能算作慢日誌分析的一個補充,如果需要完整的SQL語句文字還得依賴慢查詢日誌分析


3.2 檢視最近執行失敗的SQL


曾經有同事問,程式碼對資料庫的某些操作(比如:python的ORM模組運算元據庫)報了語法錯誤,但是程式碼並沒有記錄SQL語句文字的功能,問在MySQL資料庫層能否檢視到具體的SQL文字,看看是否有哪裡寫錯了。這個時候,大多數人首先想到的就是去檢視錯誤日誌。很遺憾,對於SQL語句的語法錯誤,錯誤日誌並不會記錄。如果你沒有完全瞭解performance_schema,那麼你很可能就會給同事回覆說:MySQL層面也並沒有記錄語法錯誤的資訊

 

實際上,performance_schema的語句事件記錄表中針對每一個語句的執行狀態都記錄了較為詳細的資訊,例如:events_statements_ 表和events_statements_summary_by_digest表(events_statements_ 表記錄的語句所有的執行錯誤資訊,但events_statements_summary_by_digest表只記錄了語句在執行過程中發生的錯誤的語句記錄統計,具體的錯誤型別不記錄,例如:語法錯誤類的不記錄),下面我們分別演示如何使用這兩個表查詢語句發生錯誤的語句資訊


首先,我們模擬一個語法錯誤的SQL,使用events_statements_history_long或者events_statements_history表查詢發生語法錯誤的SQL語句,開啟一個會話(會話1)



root@localhost : performance_schema 

05

:

18

:

09

> select * 

from

;

ERROR  1064  ( 42000 ): You have an error  in  your SQL syntax; check the manual that corresponds to your MySQL server version  for  the right syntax to use near  ''  at line  1


然後,我們查詢events_statements_history_long表中錯誤號為1064的記錄,開啟另一個會話(會話2)



root@localhost : sbtest 05:32:55> use performance_schema

Database changed
root@localhost : performance _schema 05:33:03> select THREAD_ ID,EVENT _NAME,SOURCE,sys.format_ time(TIMER _WAIT) as exec_ time,sys.format _time(LOCK_ TIME) as lock _time,SQL_ TEXT,CURRENT _SCHEMA,MESSAGE_ TEXT,ROWS _AFFECTED,ROWS_ SENT,ROWS _EXAMINED,MYSQL_ ERRNO from events _statements_ history where MYSQL_ERRNO=1064\G;
*************************** 1. row ***************************
 THREAD_ID: 119
EVENT_NAME: statement/sql/error
    SOURCE: socket_connection.cc:101
 exec_time: 71.72 us
 lock_time: 0 ps
  SQL_TEXT: select * from
CURRENT_SCHEMA: sbtest
MESSAGE_TEXT: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use
ROWS_AFFECTED: 0
ROWS_SENT: 0
ROWS_EXAMINED: 0
MYSQL_ERRNO: 1064
1 row in set (0.01 sec)


可能你不知道錯誤號是多少,可以查詢發生錯誤次數不為0的語句記錄,在裡邊找到MESSAGE_TEXT欄位提示資訊為語法錯誤的就是它了



root@localhost : performance

_schema 05:34:00> select THREAD_

ID,EVENT

_NAME,SOURCE,sys.format_

time(TIMER

_WAIT) as exec_

time,sys.format

_time(LOCK_

TIME) as lock

_time,SQL_

TEXT,CURRENT

_SCHEMA,MESSAGE_

TEXT,ROWS

_AFFECTED,ROWS_

SENT,ROWS

_EXAMINED,MYSQL_

ERRNO,errors from events

_statements_

history where errors>0\G;

*************************** 1. row ***************************
 THREAD_ID: 119
EVENT_NAME: statement/sql/error
    SOURCE: socket_connection.cc:101
 exec_time: 71.72 us
 lock_time: 0 ps
  SQL_TEXT: select * from
CURRENT_SCHEMA: sbtest
MESSAGE_TEXT: You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use
ROWS_AFFECTED: 0
ROWS_SENT: 0
ROWS_EXAMINED: 0
MYSQL_ERRNO: 1064
    errors: 1
1 row in set (0.00 sec)


使用events_statements_summary_by_digest表查詢發生語句執行錯誤的SQL語句記錄,首先,我們在會話1製造一兩個語句執行一定會發生錯誤的語句



root@localhost : sbtest 

05

:

32

:

34

> select * ;

ERROR  1096  (HY000): No tables used
root@localhost : sbtest  05 : 40 : 57 > select *  from  sbtest4 where id between  100   and   2000   and  xx= 1 ;
ERROR  1054  ( 42 S22): Unknown column  'xx'   in   'where clause'


然後,我們在events_statements_summary_by_digest表中查詢發生錯誤次數大於0的記錄,在會話2執行



root@localhost : performance

_schema 05:34:03> select SCHEMA_

NAME,DIGEST

_TEXT,COUNT_

STAR,sys.format

_time(AVG_

TIMER

_WAIT) as avg_

time,sys.format

_time(MAX_

TIMER

_WAIT) as max_

time,sys.format

_time(SUM_

LOCK

_TIME) as sum_

lock

_time,SUM_

ERRORS,FIRST

_SEEN,LAST_

SEEN from events

_statements_

summary

_by_

digest where SUM_ERRORS!=0\G;

*************************** 1. row ***************************
......
*************************** 10. row ***************************
SCHEMA_NAME: sbtest
DIGEST_TEXT: SELECT *   # 這裡就是第一個執行錯誤的語句
COUNT_STAR: 1
 avg_time: 55.14 us
 max_time: 55.14 us
sum _lock_ time: 0 ps
SUM_ERRORS: 1
FIRST_SEEN: 2018-06-25 17:40:57
LAST_SEEN: 2018-06-25 17:40:57
*************************** 11. row ***************************
SCHEMA_NAME: sbtest
DIGEST_TEXT: SELECT * FROM  `sbtest4`  WHERE  `id`  BETWEEN ? AND ? AND  `xx`  = ?   # 這裡就是第二個執行錯誤的語句
COUNT_STAR: 1
 avg_time: 101.68 us
 max_time: 101.68 us
sum _lock_ time: 0 ps
SUM_ERRORS: 1
FIRST_SEEN: 2018-06-25 17:41:03
LAST_SEEN: 2018-06-25 17:41:03
11 rows in set (0.00 sec)


PS:我們前面說過,events_statements_summary_by_digest表中不記錄具體的錯誤資訊,只做錯誤語句統計,所以,如果需要查詢到具體的錯誤資訊(如:具體的錯誤程式碼,具體的錯誤提示資訊以及具體的錯誤SQL文字等),還需要查詢events_statements_history或者events_statements_history_long表



root@localhost : performance

_schema 05:45:03> select THREAD_

ID,EVENT

_NAME,SOURCE,sys.format_

time(TIMER

_WAIT) as exec_

time,sys.format

_time(LOCK_

TIME) as lock

_time,SQL_

TEXT,CURRENT

_SCHEMA,MESSAGE_

TEXT,ROWS

_AFFECTED,ROWS_

SENT,ROWS

_EXAMINED,MYSQL_

ERRNO from events

_statements_

history where MYSQL_ERRNO!=0\G;

*************************** 1. row ***************************
......
*************************** 2. row ***************************
 THREAD_ID: 119
EVENT_NAME: statement/sql/select
    SOURCE: socket_connection.cc:101
 exec_time: 55.14 us
 lock_time: 0 ps
  SQL_TEXT: select *
CURRENT_SCHEMA: sbtest
MESSAGE_TEXT: No tables used
ROWS_AFFECTED: 0
ROWS_SENT: 0
ROWS_EXAMINED: 0
MYSQL_ERRNO: 1096
*************************** 3. row ***************************
 THREAD_ID: 119
EVENT_NAME: statement/sql/select
    SOURCE: socket_connection.cc:101
 exec_time: 101.68 us
 lock_time: 0 ps
  SQL_TEXT: select * from sbtest4 where id between 100 and 2000 and xx=1
CURRENT_SCHEMA: sbtest
MESSAGE_TEXT: Unknown column 'xx' in 'where clause'
ROWS_AFFECTED: 0
ROWS_SENT: 0
ROWS_EXAMINED: 0
MYSQL_ERRNO: 1054
3 rows in set (0.00 sec)



4. 檢視SQL執行進度資訊


MariaDB分支支援一個不依賴於performance_schema效能資料的進度展示功能,我們通過show processlist語句返回結果的最後一列就是進度資訊



root@localhost Sun Jan 

14

 

14:

08

:29

 

2018

 

14:

08

:29

 [(none)]>show processlist;

+----+------+-----------+-----------+---------+------+----------------+-------------------------------------------------+----------+
| Id |  User  | Host      |  db         | Command |  Time  | State          |  Info                                             | Progress |
+----+------+-----------+-----------+---------+------+----------------+-------------------------------------------------+----------+
|  4 |  root  | localhost |  employees  | Query  |      6   | altering table |  alter table salaries add index i_salary(salary)  |  93.939 |
|  5 |  root  | localhost |  NULL       | Query  |        | init          |  show processlist                                 |    0.000 |
+----+------+-----------+-----------+---------+------+----------------+-------------------------------------------------+----------+
2  rows  in  set ( . 00  sec)


在MySQL中也提供了類似的功能,通過階段事件中具有可預估工作量的階段事件進行記錄與計算,就可以得到一個語句的執行的階段資訊和進度資訊,下面我們分別舉例介紹如何檢視


4.1 檢視SQL執行階段


首先, 我們需要進行配置啟用,階段事件預設並未啟用,開啟一個會話(會話1)




root

@

localhost

 : performance_schema 

05

:

59

:

26

> use performance_schema

Database changed
root@localhost : performance_schema  05 : 59 : 45 > update setup_instruments set enabled= 'yes' ,timed= 'yes'  where name like  'stage/%' ;
Query   OK , 120  rows   affected  (0 .00   sec )
Rows   matched : 129  Changed : 120  Warnings : 0
root @ localhost  : performance_schema  05 : 59 : 47 > update setup_consumers set enabled= 'yes'  where name like  '%stage%' ;
Query   OK , 3  rows   affected  (0 .00   sec )
Rows   matched : 3  Changed : 3  Warnings : 0


開啟第二個會話(會話2),查詢thread_id



root@localhost : sbtest 

06

:

02

:

22

select

 sys.ps_thread_id(connection_id());

+ -----------------------------------+
| sys.ps_thread_id(connection_id()) |
+ -----------------------------------+
119  |
+ -----------------------------------+
1  row  in  set ( 0.00  sec)


先對之前舊的資訊進行清理,避免干擾(會話1)



# 先關閉其他執行緒的事件記錄功能,使用前面步驟查詢到的

thread_id


root @ localhost  : performance_schema  06 : 05 : 38 > update performance_schema.threads set INSTRUMENTED= 'NO'  where THREAD_ID!= 119 ;
Query   OK , 101  rows   affected  (0 .00   sec )
Rows   matched : 101  Changed : 101  Warnings : 0
# 清空階段事件的3張表
root @ localhost  : performance_schema  05 : 59 : 52 > truncate events_stages_current; truncate   events_stages_history ; truncate   events_stages_history_long ;
Query   OK , 0  rows   affected  (0 .00   sec )
Query   OK , 0  rows   affected  (0 .00   sec )
Query   OK , 0  rows   affected  (0 .02   sec )


現在,回到會話2執行DML語句



root@localhost : sbtest 

06

:

06

:

37

select

 count(*) from sbtest.sbtest4 where id between 

100

 

and

 

200

;

+ ----------+
| count(*) |
+ ----------+
50  |
+ ----------+
1  row  in  set ( 0.00  sec)


在會話1中查詢events_stages_history_long表



root@localhost : performance_schema 

06:10:37

> select THREAD_ID,EVENT_NAME,SOURCE,sys.format_time(TIMER_WAIT) as exec_time,WORK_COMPLETED,WORK_ESTIMATED from events_stages_history_long;

+-----------+--------------------------------+--------------------------+-----------+----------------+----------------+
| THREAD_ID |  EVENT_NAME  | SOURCE |  exec_time  | WORK_COMPLETED |  WORK_ESTIMATED  |
+-----------+--------------------------------+--------------------------+-----------+----------------+----------------+
|
  119   | stage/sql/starting |  socket_connection. cc:107   | 54.19 us |  NULL  | NULL |
| 119 |  stage/sql/checking permissions  | sql_authorization.cc:810 |   3.62  us  | NULL |  NULL  |
|
  119   | stage/sql/Opening tables |  sql_base. cc:5650   | 10.54 us |  NULL  | NULL |
| 119 |  stage/sql/init  | sql_select.cc:121 |   16.73  us  | NULL |  NULL  |
|
  119   | stage/sql/System lock |  lock. cc:323   | 4.77 us |  NULL  | NULL |
| 119 |  stage/sql/optimizing  | sql_optimizer.cc:151 |   4.78  us  | NULL |  NULL  |
|
  119   | stage/sql/statistics |  sql_optimizer. cc:367   | 50.54 us |  NULL  | NULL |
| 119 |  stage/sql/preparing  | sql_optimizer.cc:475 |   7.79  us  | NULL |  NULL  |
|
  119   | stage/sql/executing |  sql_executor. cc:119   | 381.00 ns |  NULL  | NULL |
| 119 |  stage/sql/Sending data  | sql_executor.cc:195 |   36.75  us  | NULL |  NULL  |
|
  119   | stage/sql/ end  |  sql_select. cc:199   | 931.00 ns |  NULL  | NULL |
| 119 |  stage/sql/query  end   | sql_parse.cc:4968 |   5.31  us  | NULL |  NULL  |
|
  119   | stage/sql/closing tables |  sql_parse. cc:5020   | 2.26 us |  NULL  | NULL |
| 119 |  stage/sql/freeing items  | sql_parse.cc:5596 |   8.71  us  | NULL |  NULL  |
|
  119   | stage/sql/cleaning up |  sql_parse. cc:1902   | 449.00 ns |  NULL  | NULL |
+-----------+--------------------------------+--------------------------+-----------+----------------+----------------+
15  rows  in  set ( . 01  sec)


通過以上的查詢資料可以清晰地看到一個select語句的執行全過程,以及每一個過程的時間開銷等資訊,那DDL語句的執行階段又是怎樣的呢?


先對之前舊的資訊進行清理,避免干擾(會話1)




root

@

localhost

 : performance_schema 

06

:

10

:

48

> truncate events_stages_current;

truncate

 

events_stages_history

;

truncate

 

events_stages_history_long

;

Query   OK , 0  rows   affected  (0 .00   sec )
Query   OK , 0  rows   affected  (0 .00   sec )
Query   OK , 0  rows   affected  (0 .02   sec )


然後,執行DDL語句(會話2)



root@localhost : sbtest 

03

:

37

:

32

alter table sbtest1 

add

 index 

i_c

(

c

)

;


此時,在會話1中查詢階段事件資訊(此時DDL語句並未執行完成,從最後一行記錄資訊中可以看到,WORK_COMPLETED 和WORK_ESTIMATED 列值不為NULL,表示該階段事件是一個可以度量的事件)



root@localhost : performance_schema 

06:30:04

> select THREAD_ID,EVENT_NAME,SOURCE,sys.format_time(TIMER_WAIT) as exec_time,WORK_COMPLETED,WORK_ESTIMATED from events_stages_history_long;

+-----------+------------------------------------------------------+--------------------------+-----------+----------------+----------------+
| THREAD_ID |  EVENT_NAME  | SOURCE |  exec_time  | WORK_COMPLETED |  WORK_ESTIMATED  |
+-----------+------------------------------------------------------+--------------------------+-----------+----------------+----------------+
|
  119   | stage/sql/starting |  socket_connection. cc:107   | 44.17 us |  NULL  | NULL |
| 119 |  stage/sql/checking permissions  | sql_authorization.cc:810 |   1.46  us  | NULL |  NULL  |
|
  119   | stage/sql/checking permissions |  sql_authorization. cc:810   | 2.29 us |  NULL  | NULL |
| 119 |  stage/sql/init  | sql_table.cc:9031 |   2.16  us  | NULL |  NULL  |
|
  119   | stage/sql/Opening tables |  sql_base. cc:5650   | 107.57 us |  NULL  | NULL |
| 119 |  stage/sql/setup  | sql_table.cc:9271 |   19.19  us  | NULL |  NULL  |
|
  119   | stage/sql/creating table |  sql_table. cc:5222   | 1.06 ms |  NULL  | NULL |
| 119 |  stage/sql/After create  | sql_table.cc:5355 |   76.22  us  | NULL |  NULL  |
|
  119   | stage/sql/System lock |  lock. cc:323   | 4.38 us |  NULL  | NULL |
| 119 |  stage/sql/preparing  for  alter table  | sql_table.cc:7454 |   28.63  ms  | NULL |  NULL  |
|
  119   | stage/sql/altering table |  sql_table. cc:7508   | 3.91 us |  NULL  | NULL |
| 119 |  stage/innodb/alter table (read PK  and  internal sort)  | ut0stage.h:241 |   27.09  s  | 230040 |   470155   |
+-----------+------------------------------------------------------+--------------------------+-----------+----------------+----------------+
12 rows  in  set (0.01 sec)


待到DDL語句執行完成之後,我們再次檢視階段事件資訊(會話1)



root@localhost : performance_schema 

06:31:07

> select THREAD_ID,EVENT_NAME,SOURCE,sys.format_time(TIMER_WAIT) as exec_time,WORK_COMPLETED,WORK_ESTIMATED from events_stages_history_long;

+-----------+------------------------------------------------------+--------------------------+-----------+----------------+----------------+
| THREAD_ID |  EVENT_NAME  | SOURCE |  exec_time  | WORK_COMPLETED |  WORK_ESTIMATED  |
+-----------+------------------------------------------------------+--------------------------+-----------+----------------+----------------+
......
|
  119   | stage/innodb/alter table (read PK  and  internal sort) |  ut0stage. h:241   | 27.09 s |   230040   | 470155 |
| 119 |  stage/innodb/alter table (merge sort)  | ut0stage.h:501 |   1.15  m  | 345060 |   512319   |
|
  119   | stage/innodb/alter table (insert) |  ut0stage. h:501   | 11.83 s |   460146   | 523733 |
| 119 |  stage/innodb/alter table (flush)  | ut0stage.h:501 |   18.35  s  | 523658 |   523733   |
|
  119   | stage/innodb/alter table (log apply index) |  ut0stage. h:501   | 54.63 ms |   524042   | 524042 |
| 119 |  stage/innodb/alter table (flush)  | ut0stage.h:501 |   21.18  us  | 524042 |   524042   |
|
  119   | stage/sql/committing alter table to storage engine |  sql_table. cc:7535   | 5.12 us |  NULL  | NULL |
| 119 |  stage/innodb/alter table ( end | ut0stage.h:501 |   233.52  ms  | 524042 |   524042   |
......
+-----------+------------------------------------------------------+--------------------------+-----------+----------------+----------------+
24 rows  in  set (0.01 sec)


通過以上的查詢資料可以清晰地看到一個alter語句新增索引的執行全過程,以及每一個過程的時間開銷等資訊,執行時間最長的是stage/innodb/alter table (merge sort),其次是stage/innodb/alter table (read PK and internal sort),說明在本示例中建立索引主要的時間開銷在於內部的資料排序和排序合併操作


PS:階段事件長曆史記錄表中的資料產生較快,預設的10000行配額可能很快就被打滿了,可在配置檔案中把配額調整為一個較大值,以便完整檢視DDL語句執行階段(例如:performance_schema_events_stages_history_long_size=1000000,同時要注意關掉其他不相干的任務)  


4.2 檢視SQL執行進度


在官方MySQL 版本中,performance_schema下並沒有很直觀地查詢整個語句執行進度的方法,但是可以藉助後續章節中介紹的sys.session檢視進行檢視



root@localhost : performance

_schema 04:16:38> select * from sys.session where conn_

id!=connection_id()\G;

*************************** 1. row ***************************
            thd_id: 45
          conn_id: 4
......
            state: alter table (merge sort)
              time: 30
current _statement: alter table sbtest1 add index i_ c(c)
statement_latency: 29.42 s
          progress: 46.40   # 進度百分比在這裡
      lock_latency: 2.19 ms
    rows_examined: 0
        rows_sent: 0
    rows_affected: 0
        tmp_tables: 0
  tmp _disk_ tables: 0
        full_scan: NO
......
      program_name: mysql
1 row in set (0.33 sec)



5. 檢視最近的事務執行資訊


雖然,我們可以通過慢查詢日誌查詢到一個語句的執行總時長,但,如果資料庫中存在著一些大事務執行過程中回滾了,或者說執行過程中異常終止,這個時候慢查詢日誌就愛莫能助了,這個時候我們可以藉助performance_schema的events_transactions_*表進行檢視事務相關的記錄,這些表中詳細記錄了是否有事務被回滾、活躍(長事件未提交的事務也屬於活躍事件)或已提交等,下面我們分別模擬幾種事務情況,並檢視事務事件記錄表。 


首先,我們需要進行配置啟用,事務事件預設並未啟用(會話1)




root

@

localhost

 : performance_schema 

04

:

16

:

59

> update setup_instruments set enabled=

'yes'

,timed=

'yes'

 where name like 

'transaction'

;

Query   OK , 1  row   affected  (0 .00   sec )
Rows   matched : 1   Changed : 1   Warnings : 0
root @ localhost  : performance_schema  04 : 23 : 12 > update setup_consumers set enabled= 'yes'  where name like  '%transaction%' ;
Query   OK , 3  rows   affected  (0 .00   sec )
Rows   matched : 3   Changed : 3   Warnings : 0


執行清理,避免其他事務干擾(會話1)




root

@

localhost

 : performance_schema 

04

:

30

:

25

> truncate events_transactions_current;

truncate

 

events_transactions_history

;

truncate

 

events_transactions_history_long

;

Query   OK , 0  rows   affected  (0 .00   sec )
Query   OK , 0  rows   affected  (0 .00   sec )
Query   OK , 0  rows   affected  (0 .00   sec )


然後,開啟一個新會話(會話2)用於執行事務,並模擬事務回滾




root

@

localhost

 : sbtest 

04

:

18

:

34

> use sbtest

Database changed
root@localhost : sbtest  04 : 24 : 27 > begin;
Query   OK , 0  rows   affected  (0 .00   sec )
root @ localhost  : sbtest  04 : 25 : 02 > update sbtest1 set pad= 'yyy'  where id= 1 ;
Query   OK , 1  row   affected  (0 .01   sec )
Rows   matched : 1   Changed : 1   Warnings : 0


會話1查詢活躍事務,活躍事務表示當前正在執行的事務事件,需要從events_transactions_current表查詢



root@localhost : performance

_schema 04:33:44> select THREAD_

ID,EVENT

_NAME,STATE,TRX_

ID,GTID,SOURCE,TIMER

_WAIT,ACCESS_

MODE,ISOLATION

_LEVEL,AUTOCOMMIT,NESTING_

EVENT

_ID,NESTING_

EVENT

_TYPE from events_

transactions_current\G;

*************************** 1. row ***************************
    THREAD_ID: 47
    EVENT_NAME: transaction
        STATE: ACTIVE
        TRX_ID: NULL
          GTID: AUTOMATIC
        SOURCE: transaction.cc:209
    TIMER_WAIT: 21582764879000
  ACCESS_MODE: READ WRITE
ISOLATION_LEVEL: READ COMMITTED
    AUTOCOMMIT: NO
NESTING _EVENT_ ID: 30
NESTING _EVENT_ TYPE: STATEMENT
1 row in set (0.00 sec)


會話2,回滾事務,被回滾完成的事務不再活躍




root

@

localhost

 : sbtest 

04

:

25

:

08

> rollback;

Query   OK , 0  rows   affected  (0 .01   sec )


會話1,查詢事務事件歷史記錄表events_transactions_history_long



root@localhost : performance

_schema 04:27:34> select THREAD_

ID,EVENT

_NAME,STATE,TRX_

ID,GTID,SOURCE,TIMER

_WAIT,ACCESS_

MODE,ISOLATION

_LEVEL,AUTOCOMMIT,NESTING_

EVENT

_ID,NESTING_

EVENT

_TYPE from events_

transactions

_history_

long\G;

*************************** 1. row ***************************
    THREAD_ID: 45
    EVENT_NAME: transaction
        STATE: ROLLED BACK
        TRX_ID: NULL
          GTID: AUTOMATIC
        SOURCE: transaction.cc:209
    TIMER_WAIT: 39922043951000
  ACCESS_MODE: READ WRITE
ISOLATION_LEVEL: READ COMMITTED
    AUTOCOMMIT: NO
NESTING _EVENT_ ID: 194
NESTING _EVENT_ TYPE: STATEMENT
1 row in set (0.00 sec) 


可以看到在事務事件表中記錄了一行事務事件資訊,執行緒ID為45的執行緒執行了一個事務,事務狀態為ROLLED BACK,現在,我們來模擬事務正常提交




# 會話2


root@localhost : sbtest 04:40:27> begin;
Query OK, 0 rows affected (0.00 sec)
root@localhost : sbtest 04:40:29> update sbtest1 set pad='yyy' where id=1;
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
root@localhost : sbtest 04:40:31> commit;
Query OK, 0 rows affected (0.01 sec)
# 會話1
root@localhost : performance _schema 04:38:32> select THREAD_ ID,EVENT _NAME,STATE,TRX_ ID,GTID,SOURCE,TIMER _WAIT,ACCESS_ MODE,ISOLATION _LEVEL,AUTOCOMMIT,NESTING_ EVENT _ID,NESTING_ EVENT _TYPE from events_ transactions_current\G;
*************************** 1. row ***************************
    THREAD_ID: 44
    EVENT_NAME: transaction
        STATE: COMMITTED
        TRX_ID: 421759004106352
          GTID: AUTOMATIC
        SOURCE: handler.cc:1421
    TIMER_WAIT: 87595486000
  ACCESS_MODE: READ WRITE
ISOLATION_LEVEL: READ COMMITTED
    AUTOCOMMIT: YES
NESTING _EVENT_ ID: 24003703
NESTING _EVENT_ TYPE: STATEMENT
*************************** 2. row ***************************
    THREAD_ID: 47
    EVENT_NAME: transaction
        STATE: COMMITTED
        TRX_ID: NULL
          GTID: ec123678-5e26-11e7-9d38-000c295e08a0:181879
        SOURCE: transaction.cc:209
    TIMER_WAIT: 7247256746000
  ACCESS_MODE: READ WRITE
 ISOLATION_LEVEL: READ COMMITTED
    AUTOCOMMIT: NO
NESTING _EVENT_ ID: 55
NESTING _EVENT_ TYPE: STATEMENT
2 rows in set (0.00 sec)


從上面的查詢資料可以看到,第二行事務事件記錄中的事務事件為COMMITTED狀態,表示事務已經提交成功 


PS:如果一個事務長時間未提交(長事件處於ACTIVE狀態),這種情況雖然我們從events_transactions_current表中可以查詢到未提交事務事件資訊,但是並不能很直觀地看到事務是什麼時間點開始的,我們可以藉助於information_schema.innodb_trx表來進行輔助判斷



root@localhost : performance

_schema 04:57:50> select * from information_

schema.innodb_trx\G;

*************************** 1. row ***************************
                trx_id: 2454336
            trx_state: RUNNING
          trx_started: 2018-01-14 16:43:29
trx _requested_ lock_id: NULL
      trx _wait_ started: NULL
            trx_weight: 3
  trx _mysql_ thread_id: 6
......
1 row in set (0.00 sec)



6. 檢視多執行緒複製報錯詳情


官方MySQL 從5.6版本開始支援基於庫級別的並行複製,在MySQL 5.7版本中支援基於事務的並行複製,在我們啟用了並行複製之後,如果一旦發生複製報錯,通常通過show slave status語句無法檢視到具體的報錯詳情(show slave status語句只能檢視到SQL執行緒的報錯資訊,而在多執行緒複製下,SQL執行緒的報錯資訊是根據worker執行緒的報錯資訊的一個彙總資訊),類似如下:



admin@localhost : (none) 

12

:

45

:

19

> show slave 

status

\G;

............
              Last_Errno:  1062
              Last_Error: Coordinator stopped because there were  error (s)  in  the worker(s). The most recent failure being: Worker  1  failed executing transaction  '23fb5832-e4bc-11e7-8ea4-525400a4b2e1:2553990'  at master  log  mysql-bin .000034 , end_log_pos  98797.  See  error   log   and / or  performance_schema.replication_applier_status_by_worker  table   for  more details about this failure  or  others,  if  any.
............
          Last_SQL_Errno:  1062
          Last_SQL_Error: Coordinator stopped because there were  error (s)  in  the worker(s). The most recent failure being: Worker  1  failed executing transaction  '23fb5832-e4bc-11e7-8ea4-525400a4b2e1:2553990'  at master  log  mysql-bin .000034 , end_log_pos  98797.  See  error   log   and / or  performance_schema.replication_applier_status_by_worker  table   for  more details about this failure  or  others,  if  any.
............
1  row  in  set ( 0.00  sec)


根據報錯提示檢視performance_schema.replication_applier_status_by_worker表,該表中詳細記錄了每一個worker執行緒的詳細資訊,從這裡我們就可以找到發生報錯的worker執行緒具體的報錯原因



admin@localhost : (none) 12:51:53> select * from performance

_schema.replication_

applier

_status_

by

_worker where LAST_

ERROR_MESSAGE!=''\G;

*************************** 1. row ***************************
    CHANNEL_NAME: 
        WORKER_ID: 2
        THREAD_ID: NULL
    SERVICE_STATE: OFF
LAST _SEEN_ TRANSACTION: 23fb5832-e4bc-11e7-8ea4-525400a4b2e1:2553991
LAST _ERROR_ NUMBER: 1062
LAST _ERROR_ MESSAGE: Worker 2 failed executing transaction '23fb5832-e4bc-11e7-8ea4-525400a4b2e1:2553991' at master log mysql-bin.000034, end _log_ pos 99514; Could not execute Write _rows event on table sbtest.sbtest4; Duplicate entry '833353' for key 'PRIMARY', Error_ code: 1062; handler error HA _ERR_ FOUND _DUPP_ KEY; the event's master log FIRST, end _log_ pos 99514
LAST _ERROR_ TIMESTAMP: 2018-01-02 14:08:58
1 row in set (0.00 sec)


從查詢performance_schema.replication_applier_status_by_worker表可以發現,具體的複製報錯資訊是因為主鍵衝突了


PS:由於歷史原因,performance_schema中的複製資訊記錄表只記錄跟GTID相關的資訊,而在mysql系統字典庫下的slave_master_info、slave_relay_log_info、slave_worker_info表記錄的是跟binlog position相關的資訊。另外,如果選擇相關的複製資訊記錄到檔案,那麼磁碟上還存在著master.info、relay_log.info等檔案記錄binlog position相關的資訊









來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/26736162/viewspace-2651257/,如需轉載,請註明出處,否則將追究法律責任。

相關文章