Example:
mysql [information_schema] > select trx_id,trx_state
-> from information_schema.innodb_trx; +--------+-----------+ | trx_id | trx_state | +--------+-----------+ | 7974 | LOCK WAIT | | 7962 | RUNNING | +--------+-----------+ 2 rows in set (0.00 sec)
Then I made a query to join a few information_schema and performance_schema tables to find out which query was blocking my transactions. It turned out that the blocking transaction had a trx_query=NONE. So my query was block by a transaction doing nothing. That's not really helpful.
Let's try to recreate the situation and see exactly what happened. I'll use two sessions for the transactions and a third to monitor the server.
The first session:
mysql [test] > create table t1 (id int); Query OK, 0 rows affected (0.01 sec) mysql [test] > insert into t1 values(1),(2),(3); Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql [test] > create temporary table t2 (id int); Query OK, 0 rows affected (0.01 sec) mysql [test] > start transaction; Query OK, 0 rows affected (0.00 sec) mysql [test] > insert into t2 select * from t1; Query OK, 3 rows affected (0.00 sec) Records: 3 Duplicates: 0 Warnings: 0
The second session:
mysql [test] > insert into t1 values(4);Now the second session is waiting.
Let's start a third session to see what's happening. If the second session hits a wait timeout, you can just restart it.
mysql [information_schema] > select * from innodb_trx\G
*************************** 1. row ***************************
trx_id: 8022
trx_state: LOCK WAIT
trx_started: 2014-10-25 11:38:25
trx_requested_lock_id: 8022:18:3:1
trx_wait_started: 2014-10-25 11:38:25
trx_weight: 2
trx_mysql_thread_id: 19
trx_query: insert into t1 values(4)
trx_operation_state: inserting
trx_tables_in_use: 1
trx_tables_locked: 1
trx_lock_structs: 2
trx_lock_memory_bytes: 360
trx_rows_locked: 1
trx_rows_modified: 0
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLE READ
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_adaptive_hash_latched: 0
trx_adaptive_hash_timeout: 10000
trx_is_read_only: 0
trx_autocommit_non_locking: 0
*************************** 2. row ***************************
trx_id: 8020
trx_state: RUNNING
trx_started: 2014-10-25 11:35:07
trx_requested_lock_id: NULL
trx_wait_started: NULL
trx_weight: 6
trx_mysql_thread_id: 1
trx_query: NULL
trx_operation_state: NULL
trx_tables_in_use: 0
trx_tables_locked: 0
trx_lock_structs: 3
trx_lock_memory_bytes: 360
trx_rows_locked: 4
trx_rows_modified: 3
trx_concurrency_tickets: 0
trx_isolation_level: REPEATABLE READ
trx_unique_checks: 1
trx_foreign_key_checks: 1
trx_last_foreign_key_error: NULL
trx_adaptive_hash_latched: 0
trx_adaptive_hash_timeout: 10000
trx_is_read_only: 0
trx_autocommit_non_locking: 0
2 rows in set (0.00 sec)
mysql [information_schema] > select * from innodb_lock_waits\G
*************************** 1. row ***************************
requesting_trx_id: 8022
requested_lock_id: 8022:18:3:1
blocking_trx_id: 8020
blocking_lock_id: 8020:18:3:1
1 row in set (0.00 sec)
mysql [information_schema] > select * from innodb_locks\G
*************************** 1. row ***************************
lock_id: 8022:18:3:1
lock_trx_id: 8022
lock_mode: X
lock_type: RECORD
lock_table: `test`.`t1`
lock_index: GEN_CLUST_INDEX
lock_space: 18
lock_page: 3
lock_rec: 1
lock_data: supremum pseudo-record
*************************** 2. row ***************************
lock_id: 8020:18:3:1
lock_trx_id: 8020
lock_mode: S
lock_type: RECORD
lock_table: `test`.`t1`
lock_index: GEN_CLUST_INDEX
lock_space: 18
lock_page: 3
lock_rec: 1
lock_data: supremum pseudo-record
2 rows in set (0.00 sec)
mysql [information_schema] > use sys
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql [sys] > select * from innodb_lock_waits\G
*************************** 1. row ***************************
waiting_trx_id: 8022
waiting_thread: 19
waiting_query: insert into t1 values(4)
waiting_lock_id: 8022:18:3:1
waiting_lock_mode: X
waiting_lock_type: RECORD
waiting_lock_table: `test`.`t1`
waiting_lock_index: GEN_CLUST_INDEX
blocking_trx_id: 8020
blocking_thread: 1
blocking_query: NULL
blocking_lock_id: 8020:18:3:1
blocking_lock_mode: S
blocking_lock_type: RECORD
blocking_lock_table: `test`.`t1`
blocking_lock_index: GEN_CLUST_INDEX
1 row in set (0.00 sec)
The last query is from the SYS schema. You'll need version 1.3.0 of SYS for this to work.
So we now know which transaction is blocking which transaction, but we don't yet know what that transaction is doing as blocking_query is NULL.
But we have blocking_thread=1. With performance_schema we can find the statements executed in that thread:
mysql [performance_schema] > SELECT SQL_TEXT FROM events_statements_history_long
-> WHERE thread_id=(SELECT thread_id FROM threads WHERE processlist_id=1)
-> order by TIMER_START; +---------------------------------+ | SQL_TEXT | +---------------------------------+ | rollback | | start transaction | | insert into t2 select * from t1 | +---------------------------------+ 3 rows in set (0.00 sec)
You should be aware that there are 3 ID's with which we are dealing:
- The InnoDB Transaction ID (trx_id)
- The MySQL Processlist ID (information_schema.processlist.ID, used in blocking_thread)
- The MySQL Thread ID (performance_schema.threads.thread_id)
It might also be possible to find OS pid if the application uses a connector which supports connection attributes. The host on which this pid lives can be found in the processlist.
mysql [performance_schema] > select * from session_connect_attrs where PROCESSLIST_ID=2; +----------------+-----------------+----------------+------------------+ | PROCESSLIST_ID | ATTR_NAME | ATTR_VALUE | ORDINAL_POSITION | +----------------+-----------------+----------------+------------------+ | 2 | _os | linux-glibc2.5 | 0 | | 2 | _client_name | libmysql | 1 | | 2 | _pid | 25415 | 2 | | 2 | _client_version | 5.6.21 | 3 | | 2 | _platform | x86_64 | 4 | | 2 | program_name | mysql | 5 | +----------------+-----------------+----------------+------------------+ 6 rows in set (0.00 sec)
For this procedure to succeed you'll need this:
- MySQL 5.6 or newer.
- performance_schema must be enabled
- In setup_consumers events_statements_history_long must be enabled.
- The performance_schema_events_statements_history_long_size variable must be set large enough. Changing this requires a restart of the server. With this variable you specify how many statements there are kept in the events_statements_history_long table. You probably need to raise this on a busy server to get the statements of the blocking transaction as the transaction might be waiting for quite some time.
For viewing the statements a transaction has done previously, I generally recommend enabling and using the events_statements_history consumer/table, which holds the last 10 statements per active thread, rather than events_statements_history_long, which is the last 10000 globally (which could age out quickly). This will you will *always* get the last 10 statements per thread.
ReplyDeleteGood one. Thanks.
DeleteThere are Multiple Queries Executed on the same table in events_statements_history_long , How can i know which one among them ??
ReplyDeleteWe use tomcat connection pool , to get the connections for our service