MySQL Transaction--查看未提交事務執行的SQL


未提交事務

長期未提交事務,指開啟事務后,長時間未向MySQL發出SQL執行請求或事務處理(COMMIT/ROLLBACK)請求,在系統表`information_schema`.`INNODB_TRX` 中狀態為RUNNING,而在`information_schema`.`PROCESSLIST`中狀態為SlEEP。

## 查看未提交的事務(3秒內未操作的事務)
SELECT 
p.ID AS conn_id,
P.USER AS login_user,
P.HOST AS login_host,
p.DB AS database_name,
P.TIME AS trx_sleep_seconds,
TIME_TO_SEC(TIMEDIFF(NOW(),T.trx_started)) AS trx_open_seconds,
T.trx_started,
T.trx_isolation_level,
T.trx_tables_locked,
T.trx_rows_locked,
t.trx_state,
p.COMMAND AS process_state
FROM `information_schema`.`INNODB_TRX` t
INNER JOIN `information_schema`.`PROCESSLIST` p
ON t.trx_mysql_thread_id=p.id
WHERE t.trx_state='RUNNING'
AND p.COMMAND='Sleep'
AND P.TIME>3
ORDER BY T.trx_started ASC \G

 

導致事務長期未提交的因素很多,常見的有:

1、事務過程中執行其他非數據庫操作,導致事務長期未被處理。
2、事務處理異常或實現邏輯有誤,導致事務未被正常處理。
3、網段異常導致應用端請求未被正常發送給數據庫,數據庫等待應用后續操作。
4、應用服務器性能問題(如CPU爆滿),導致應用無法及時切換到該進程進行處理。

 

對於代碼實現邏輯有誤的問題,如果DBA能提供該事務執行的SQL語句將有助於研發快遞定位,哪如何定位呢?

方式1:使用通用日志

## 查看general log配置
show variables like '%general_log%'

## 開啟general log
SET GLOBAL general_log = 1;

在通用日志中能看到

2019-04-29T13:52:07.932646+08:00    20036 Query    SELECT * FROM `sys`.`processlist`
2019-04-29T13:52:36.049694+08:00    20036 Query    SELECT * FROM `sys`.`processlist` WHERE CONN_ID=2036
2019-04-29T13:52:44.378687+08:00    20036 Query    SELECT * FROM `sys`.`processlist` WHERE CONN_ID=20040
2019-04-29T13:52:51.418669+08:00    20036 Query    SELECT * FROM `sys`.`processlist` WHERE CONN_ID=20040

上面的20036是會話連接號(connection id), 使用SHOW PROCESSLIST 或`information_schema`.`PROCESSLIST`查到的ID即該ID。

 

對於MySQL 5.7版本,可以使用sys.session視圖來查看會話最后一次執行的SQL:

SELECT * 
FROM sys.session 
WHERE CONN_ID = 20036 \G

輸出結果為:

            thd_id: 20065
           conn_id: 20040
              user: admin@172.28.37.35
                db: demodb
           command: Sleep
             state: NULL
              time: 19
 current_statement: NULL
 statement_latency: NULL
          progress: NULL
      lock_latency: 157.00 us
     rows_examined: 110
         rows_sent: 110
     rows_affected: 0
        tmp_tables: 0
   tmp_disk_tables: 0
         full_scan: YES
    last_statement: select * from tb001 limit 110
last_statement_latency: 607.34 us
    current_memory: 0 bytes
         last_wait: NULL
 last_wait_latency: NULL
            source: NULL
       trx_latency: NULL
         trx_state: NULL
    trx_autocommit: NULL
               pid: 12758
      program_name: mysql

 

使用系統視圖sys.processlist 也能看到類似上面的結果:

SELECT *  
FROM sys.processlist 
WHERE CONN_ID = 20040

 

系統視圖sys.processlist中數據主要來源於:

performance_schema.events_statements_current
performance_schema.events_statements_history

且上面兩個表中記錄的數據需要使用線程號(THREAD ID),可以使用performance_schema.threads來與連接號(CONNECTION ID/PROCESSLIST ID)進行關聯。

 

因此可以將查詢未提交事務的SQL優化為:

## 查看未提交的事務(3秒內未操作的事務)
SELECT 
p.ID AS conn_id,
P.USER AS login_user,
P.HOST AS login_host,
p.DB AS database_name,
P.TIME AS trx_sleep_seconds,
TIME_TO_SEC(TIMEDIFF(NOW(),T.trx_started)) AS trx_open_seconds,
T.trx_started,
T.trx_isolation_level,
T.trx_tables_locked,
T.trx_rows_locked,
t.trx_state,
p.COMMAND AS process_state,
(
SELECT GROUP_CONCAT(REPLACE(REPLACE(REPLACE(T1.`SQL_TEXT`,'\n',' '),'\r',' '),'\t',' ') SEPARATOR ';
') 
FROM performance_schema.events_statements_history AS T1
INNER JOIN performance_schema.threads AS T2
ON T1.`THREAD_ID`=T2.`THREAD_ID`
WHERE T2.`PROCESSLIST_ID`=P.id
) AS trx_sql_text
FROM `information_schema`.`INNODB_TRX` t
INNER JOIN `information_schema`.`PROCESSLIST` p
ON t.trx_mysql_thread_id=p.id
WHERE t.trx_state='RUNNING'
AND p.COMMAND='Sleep'
AND P.TIME>3
ORDER BY T.trx_started ASC \G

輸出結果中trx_sql_text即該事務執行過的SQL語句。

 

查看每個未提交事務執行過多的所有SQL

SELECT @dt_ts:=UNIX_TIMESTAMP(NOW());
SELECT 
@dt_timer:=MAX(SH.TIMER_START)
FROM performance_schema.threads AS T1
INNER JOIN performance_schema.events_statements_history AS SH
ON T1.`THREAD_ID`=SH.`THREAD_ID`
WHERE T1.PROCESSLIST_ID=CONNECTION_ID();

SELECT 
SH.CURRENT_SCHEMA AS database_name,
REPLACE(REPLACE(REPLACE(SH.`SQL_TEXT`,'\n',' '),'\r',' '),'\t',' ') AS executed_sql,
FROM_UNIXTIME(@dt_ts-CAST((@dt_timer-SH.TIMER_START)/1000000000000 AS SIGNED)) AS start_time,
FROM_UNIXTIME(@dt_ts-CAST((@dt_timer+SH.TIMER_END)/1000000000000 AS SIGNED)) AS end_time,
(SH.TIMER_END-SH.TIMER_START)/1000000000000 AS used_seconds,
SH.TIMER_WAIT/1000000000000 AS wait_seconds,
SH.LOCK_TIME/1000000000000 AS lock_seconds,
SH.ROWS_AFFECTED AS affected_rows,
SH.ROWS_SENT AS send_rows
FROM performance_schema.threads AS T1
INNER JOIN performance_schema.events_statements_history AS SH
ON T1.`THREAD_ID`=SH.`THREAD_ID`
WHERE T1.PROCESSLIST_ID IN (
SELECT 
p.ID AS conn_id
FROM `information_schema`.`INNODB_TRX` t
INNER JOIN `information_schema`.`PROCESSLIST` p
ON t.trx_mysql_thread_id=p.id
WHERE t.trx_state='RUNNING'
AND p.COMMAND='Sleep'
AND P.TIME>3
)
AND SH.TIMER_START<@dt_timer
ORDER BY SH.TIMER_START ASC;

 

PS:如果使用MySQL 5.7早期版本熱備升級到MySQL 5.7后期版本,可能會導致sys庫上很多視圖和函數無法正常使用,需要使用mysql_upgrade進行升級。

/export/servers/mysql/bin/mysql_upgrade \
--host='127.0.0.1' \
--port=3305 \
--user='admin' \
--password="admin_psw"

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM