There are different ways to study metadata locks in MySQL and MariaDB, as I once described in details. Until recently MariaDB had not provided the performance_schema.metadata_locks table, but it was finally added in 10.5. So, now you can easily get the details in a way that became the easiest and most well known since MySQL 5.7. You just have to make sure performance_schema is enabled at startup:
openxs@ao756:~/dbs/maria10.5$ bin/mysqld_safe --no-defaults --port=3311 --socket=/tmp/mariadb105.sock --performance_schema=1 &
[1] 23626
openxs@ao756:~/dbs/maria10.5$ 200927 18:26:41 mysqld_safe Logging to '/home/openxs/dbs/maria10.5/data/ao756.err'.
200927 18:26:41 mysqld_safe Starting mariadbd daemon with databases from /home/openxs/dbs/maria10.5/data
openxs@ao756:~/dbs/maria10.5$ bin/mysql --socket=/tmp/mariadb105.sock
Welcome to the MariaDB monitor. Commands end with ; or \g.
Your MariaDB connection id is 3
Server version: 10.5.6-MariaDB Source distribution
Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
MariaDB [(none)]> select @@performance_schema;
+----------------------+
| @@performance_schema |
+----------------------+
| 1 |
+----------------------+
1 row in set (0,000 sec)
MariaDB [(none)]> use performance_schema
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
MariaDB [performance_schema]> show tables like '%metadata%';
+-------------------------------------------+
| Tables_in_performance_schema (%metadata%) |
+-------------------------------------------+
| metadata_locks |
+-------------------------------------------+
1 row in set (0,001 sec)
MariaDB [performance_schema]> select * from setup_instruments where name like '%metadata%';
+---------------------------------------------------------+---------+-------+
| NAME | ENABLED | TIMED |
+---------------------------------------------------------+---------+-------+
| wait/io/file/csv/metadata | YES | YES |
| stage/sql/Waiting for schema metadata lock | NO | NO |
| stage/sql/Waiting for table metadata lock | NO | NO |
| stage/sql/Waiting for stored function metadata lock | NO | NO |
| stage/sql/Waiting for stored procedure metadata lock | NO | NO |
| stage/sql/Waiting for stored package body metadata lock | NO | NO |
| stage/sql/Waiting for trigger metadata lock | NO | NO |
| stage/sql/Waiting for event metadata lock | NO | NO |
| memory/performance_schema/metadata_locks | YES | NO |
| wait/lock/metadata/sql/mdl | NO | NO |
+---------------------------------------------------------+---------+-------+
10 rows in set (0,034 sec)
So, the metadata_locks table is there and we just need to enable wait/lock/metadata/sql/mdl instrument:
MariaDB [performance_schema]> update setup_instruments set enabled = 'YES', timed='YES' where name like 'wait%mdl';
Query OK, 1 row affected (0,030 sec)
Rows matched: 1 Changed: 1 Warnings: 0
MariaDB [performance_schema]> select * from metadata_locks\G
*************************** 1. row ***************************
OBJECT_TYPE: TABLE
OBJECT_SCHEMA: performance_schema
OBJECT_NAME: metadata_locks
OBJECT_INSTANCE_BEGIN: 140032981253664
LOCK_TYPE: SHARED_READ
LOCK_DURATION: TRANSACTION
LOCK_STATUS: GRANTED
SOURCE:
OWNER_THREAD_ID: 13
OWNER_EVENT_ID: 1
1 row in set (0,001 sec)
MariaDB [performance_schema]> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
| 3 |
+-----------------+
1 row in set (0,000 sec)
MariaDB [performance_schema]> select * from threads where processlist_id = 3\G
*************************** 1. row ***************************
THREAD_ID: 13
NAME: thread/sql/one_connection
TYPE: FOREGROUND
PROCESSLIST_ID: 3
PROCESSLIST_USER: openxs
PROCESSLIST_HOST: localhost
PROCESSLIST_DB: performance_schema
PROCESSLIST_COMMAND: Query
PROCESSLIST_TIME: 0
PROCESSLIST_STATE: Sending data
PROCESSLIST_INFO: select * from threads where processlist_id = 3
PARENT_THREAD_ID: 1
ROLE: NULL
INSTRUMENTED: YES
HISTORY: YES
CONNECTION_TYPE: Socket
THREAD_OS_ID: 23719
1 row in set (0,018 sec)
The output above shows that one has to join to threads table on thread_id = metadata_locks.owner_thread_id to get the details for thread that set metadata lock:
MariaDB [performance_schema]> SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, THREAD_ID, PROCESSLIST_ID, PROCESSLIST_INFO FROM performance_schema.metadata_locks INNER JOIN performance_schema.threads ON THREAD_ID = OWNER_THREAD_ID WHERE PROCESSLIST_ID <> CONNECTION_ID()\G
*************************** 1. row ***************************
OBJECT_TYPE: BACKUP
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
LOCK_TYPE: BACKUP_DDL
LOCK_STATUS: PENDING
THREAD_ID: 50
PROCESSLIST_ID: 21
PROCESSLIST_INFO: drop database test
1 row in set (0,001 sec)
In the cases above we see the lock, SHARED_READ, on the table we access, performance_schema.metadata_locks, set by thread that reads from the table. Then we see how to avoid seeing locks set by current connection and how to get current statement (or other SHOW PROCESSLIST details) for the thread associated with the lock.
I noted that SOURCE column in the metadata_locks table is empty. It seems to be the case for other locks too, like those caused by sysbench test running:
MariaDB [performance_schema]> select * from metadata_locks limit 5\G
*************************** 1. row ***************************
OBJECT_TYPE: TABLE
OBJECT_SCHEMA: sbtest
OBJECT_NAME: sbtest1
OBJECT_INSTANCE_BEGIN: 140032787904800
LOCK_TYPE: SHARED_WRITE
LOCK_DURATION: TRANSACTION
LOCK_STATUS: GRANTED
SOURCE:
OWNER_THREAD_ID: 34
OWNER_EVENT_ID: 1
*************************** 2. row ***************************
OBJECT_TYPE: BACKUP
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140032778203712
LOCK_TYPE: BACKUP_TRANS_DML
LOCK_DURATION: STATEMENT
LOCK_STATUS: GRANTED
SOURCE:
OWNER_THREAD_ID: 34
OWNER_EVENT_ID: 1
*************************** 3. row ***************************
OBJECT_TYPE: TABLE
OBJECT_SCHEMA: sbtest
OBJECT_NAME: sbtest1
OBJECT_INSTANCE_BEGIN: 140033249782768
LOCK_TYPE: SHARED_WRITE
LOCK_DURATION: TRANSACTION
LOCK_STATUS: GRANTED
SOURCE:
OWNER_THREAD_ID: 33
OWNER_EVENT_ID: 1
*************************** 4. row ***************************
OBJECT_TYPE: BACKUP
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140033248057584
LOCK_TYPE: BACKUP_TRANS_DML
LOCK_DURATION: STATEMENT
LOCK_STATUS: GRANTED
SOURCE:
OWNER_THREAD_ID: 33
OWNER_EVENT_ID: 1
*************************** 5. row ***************************
OBJECT_TYPE: TABLE
OBJECT_SCHEMA: sbtest
OBJECT_NAME: sbtest1
OBJECT_INSTANCE_BEGIN: 140033449398688
LOCK_TYPE: SHARED_WRITE
LOCK_DURATION: TRANSACTION
LOCK_STATUS: GRANTED
SOURCE:
OWNER_THREAD_ID: 32
OWNER_EVENT_ID: 1
5 rows in set (0,001 sec)
MariaDB [performance_schema]>
I do not think that it is normal, as in MySQL 8.0.21, for example, I still see the reference to the line of source code where the lock is set:
openxs@ao756:~/dbs/8.0$ bin/mysql --socket=/tmp/mysql8.sock -uroot performance_schema
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 8
Server version: 8.0.21 Source distribution
Copyright (c) 2000, 2020, Oracle and/or its affiliates. All rights reserved.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
mysql> flush tables with read lock;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from metadata_locks\G
*************************** 1. row ***************************
OBJECT_TYPE: GLOBAL
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
COLUMN_NAME: NULL
OBJECT_INSTANCE_BEGIN: 139972651847648
LOCK_TYPE: SHARED
LOCK_DURATION: EXPLICIT
LOCK_STATUS: GRANTED
SOURCE: lock.cc:1033
OWNER_THREAD_ID: 49
OWNER_EVENT_ID: 112
*************************** 2. row ***************************
OBJECT_TYPE: COMMIT
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
COLUMN_NAME: NULL
OBJECT_INSTANCE_BEGIN: 139972651711440
LOCK_TYPE: SHARED
LOCK_DURATION: EXPLICIT
LOCK_STATUS: GRANTED
SOURCE: lock.cc:1108
OWNER_THREAD_ID: 49
OWNER_EVENT_ID: 112
...
I reported this immediately as a bug, MDEV-23827 - "performance_schema.metadata_locks.source column is always empty".
The details in this table that are not covered by the MySQL manual require some investigation. You probably noted BACKUP_TRANS_DML as a lock type above. Another example (that you may get while mariabackup a.k.a mariadb-backup in 10.5 is running):
MariaDB [performance_schema]> select * from metadata_locks\G
*************************** 1. row ***************************
OBJECT_TYPE: BACKUP
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140032980286816
LOCK_TYPE: BACKUP_START
LOCK_DURATION: EXPLICIT
LOCK_STATUS: GRANTED
SOURCE:
OWNER_THREAD_ID: 44
OWNER_EVENT_ID: 1
...
I've investigated similar details in the past for MySQL and Percona Server, by code review and some gdb tests. I'd expect to see these values documented somewhere in the mdl.h file in the source code. Here is what we have:
openxs@ao756:~/git/server$ grep -rn BACKUP_TRANS_DML *
mysql-test/main/mdl.result:13:MDL_BACKUP_TRANS_DML Backup lock
mysql-test/main/mdl.result:19:MDL_BACKUP_TRANS_DML Backup lock
sql/mdl.h:295:#define MDL_BACKUP_TRANS_DML enum_mdl_type(8)
sql/mdl.cc:134: { C_STRING_WITH_LEN("MDL_BACKUP_TRANS_DML") },
sql/mdl.cc:490: MDL_BIT(MDL_BACKUP_TRANS_DML)));
sql/mdl.cc:1633: MDL_BIT(MDL_BACKUP_DML) | MDL_BIT(MDL_BACKUP_TRANS_DML) | MDL_BIT(MDL_BACKUP_SYS_DML) | MDL_BIT(MDL_BACKUP_DDL) | MDL_BIT(MDL_BACKUP_ALTER_COPY),
sql/mdl.cc:1634: MDL_BIT(MDL_BACKUP_DML) | MDL_BIT(MDL_BACKUP_TRANS_DML) | MDL_BIT(MDL_BACKUP_SYS_DML) | MDL_BIT(MDL_BACKUP_DDL) | MDL_BIT(MDL_BACKUP_ALTER_COPY) | MDL_BIT(MDL_BACKUP_COMMIT),
sql/sql_base.cc:2048: mdl_type= MDL_BACKUP_TRANS_DML;
storage/perfschema/table_helper.cc:636: case MDL_BACKUP_TRANS_DML:
storage/perfschema/table_helper.cc:637: PFS_engine_table::set_field_varchar_utf8(f, STRING_WITH_LEN("BACKUP_TRANS_DML"));
openxs@ao756:~/git/server$
This is what we have in sql/mdl.h in MariaDB 10.5:
258 /** Backup locks */
259
260 /**
261 Block concurrent backup
262 */
263 #define MDL_BACKUP_START enum_mdl_type(0)
264 /**
265 Block new write requests to non transactional tables
266 */
267 #define MDL_BACKUP_FLUSH enum_mdl_type(1)
268 /**
269 In addition to previous locks, blocks running requests to non trans tables
270 Used to wait until all DML usage of on trans tables are finished
271 */
272 #define MDL_BACKUP_WAIT_FLUSH enum_mdl_type(2)
273 /**
274 In addition to previous locks, blocks new DDL's from starting
275 */
276 #define MDL_BACKUP_WAIT_DDL enum_mdl_type(3)
277 /**
278 In addition to previous locks, blocks commits
279 */
280 #define MDL_BACKUP_WAIT_COMMIT enum_mdl_type(4)
281
282 /**
283 Blocks (or is blocked by) statements that intend to modify data. Acquired
284 before commit lock by FLUSH TABLES WITH READ LOCK.
285 */
286 #define MDL_BACKUP_FTWRL1 enum_mdl_type(5)
287
288 /**
289 Blocks (or is blocked by) commits. Acquired after global read lock by
290 FLUSH TABLES WITH READ LOCK.
291 */
292 #define MDL_BACKUP_FTWRL2 enum_mdl_type(6)
293
294 #define MDL_BACKUP_DML enum_mdl_type(7)
295 #define MDL_BACKUP_TRANS_DML enum_mdl_type(8)
296 #define MDL_BACKUP_SYS_DML enum_mdl_type(9)
...
I see creative re-use of the values from enum_mdl_type.
The last bug not the least, this is how the global read lock and the metadata lock wait look like:
MariaDB [performance_schema]> flush tables with read lock;
Query OK, 0 rows affected (0,090 sec)
MariaDB [performance_schema]> select * from metadata_locks\G
*************************** 1. row ***************************
OBJECT_TYPE: BACKUP
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140033115590992
LOCK_TYPE: BACKUP_FTWRL1
LOCK_DURATION: EXPLICIT
LOCK_STATUS: GRANTED
SOURCE:
OWNER_THREAD_ID: 56
OWNER_EVENT_ID: 1
...
MariaDB [performance_schema]> select * from metadata_locks\G
*************************** 1. row ***************************
OBJECT_TYPE: BACKUP
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140033115590992
LOCK_TYPE: BACKUP_FTWRL1
LOCK_DURATION: EXPLICIT
LOCK_STATUS: GRANTED
SOURCE:
OWNER_THREAD_ID: 56
OWNER_EVENT_ID: 1
*************************** 2. row ***************************
OBJECT_TYPE: BACKUP
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
OBJECT_INSTANCE_BEGIN: 140032979653216
LOCK_TYPE: BACKUP_DDL
LOCK_DURATION: STATEMENT
LOCK_STATUS: PENDING
SOURCE:
OWNER_THREAD_ID: 50
OWNER_EVENT_ID: 1
*************************** 3. row ***************************
OBJECT_TYPE: TABLE
OBJECT_SCHEMA: performance_schema
OBJECT_NAME: metadata_locks
OBJECT_INSTANCE_BEGIN: 140033113928032
LOCK_TYPE: SHARED_READ
LOCK_DURATION: TRANSACTION
LOCK_STATUS: GRANTED
SOURCE:
OWNER_THREAD_ID: 56
OWNER_EVENT_ID: 1
3 rows in set (0,000 sec)
MariaDB [performance_schema]> select * from information_schema.metadata_lock_info\G
*************************** 1. row ***************************
THREAD_ID: 23
LOCK_MODE: MDL_BACKUP_FTWRL2
LOCK_DURATION: NULL
LOCK_TYPE: Backup lock
TABLE_SCHEMA:
TABLE_NAME:
1 row in set (0,001 sec)
The last statement above shows that the old way to check metadata locks, via metadata_lock_info plugin, had provided much less details. We could not even distinguish lock from lock wait (pending lock) with it!
Are there any locks on that bridge? Go figure... |
Finally, even with metadata_locks table in place, finding the details about the exact blocking lock may be not trivial. See this blog post for some idea on how to do this based on assumption that blocking lock is surely set earlier than pending one.
For those interested, the same situation is reported differently by MariaDB:
ReplyDeleteMariaDB [test]> SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, THREAD_ID, PROCESSLIST_ID, PROCESSLIST_INFO FROM performance_schema.metadata_locks INNER JOIN performance_schema.threads ON THREAD_ID = OWNER_THREAD_ID WHERE PROCESSLIST_ID <> CONNECTION_ID()\G
*************************** 1. row ***************************
OBJECT_TYPE: TABLE
OBJECT_SCHEMA: test
OBJECT_NAME: t
LOCK_TYPE: SHARED_WRITE
LOCK_STATUS: GRANTED
THREAD_ID: 72
PROCESSLIST_ID: 27
PROCESSLIST_INFO: update t set c1 = sleep(20)
*************************** 2. row ***************************
OBJECT_TYPE: BACKUP
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
LOCK_TYPE: BACKUP_TRANS_DML
LOCK_STATUS: GRANTED
THREAD_ID: 72
PROCESSLIST_ID: 27
PROCESSLIST_INFO: update t set c1 = sleep(20)
*************************** 3. row ***************************
OBJECT_TYPE: BACKUP
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
LOCK_TYPE: BACKUP_FTWRL1
LOCK_STATUS: PENDING
THREAD_ID: 77
PROCESSLIST_ID: 28
PROCESSLIST_INFO: set global read_only = 1
3 rows in set (0,001 sec)
vs MySQL 8:
MySQL [test]> SELECT OBJECT_TYPE, OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, THREAD_ID, PROCESSLIST_ID, PROCESSLIST_INFO FROM performance_schema.metadata_locks INNER JOIN performance_schema.threads ON THREAD_ID = OWNER_THREAD_ID WHERE PROCESSLIST_ID <> CONNECTION_ID()\G
*************************** 1. row ***************************
OBJECT_TYPE: GLOBAL
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
LOCK_TYPE: SHARED
LOCK_STATUS: PENDING
THREAD_ID: 51
PROCESSLIST_ID: 10
PROCESSLIST_INFO: set global read_only=1
*************************** 2. row ***************************
OBJECT_TYPE: GLOBAL
OBJECT_SCHEMA: NULL
OBJECT_NAME: NULL
LOCK_TYPE: INTENTION_EXCLUSIVE
LOCK_STATUS: GRANTED
THREAD_ID: 52
PROCESSLIST_ID: 11
PROCESSLIST_INFO: update t set c1 = sleep(20)
*************************** 3. row ***************************
OBJECT_TYPE: TABLE
OBJECT_SCHEMA: test
OBJECT_NAME: t
LOCK_TYPE: SHARED_WRITE
LOCK_STATUS: GRANTED
THREAD_ID: 52
PROCESSLIST_ID: 11
PROCESSLIST_INFO: update t set c1 = sleep(20)
3 rows in set (0,001 sec)
Hence this MariaDB documentation request:
https://jira.mariadb.org/browse/MDEV-23882