"MySQL Practical Combat 45 Lectures" - Study Notes 19 "SQL query for slow execution, lock waiting/consistency reading" [recommended collection]

Due to the writing problem of SQL itself (such as joining too many tables, not using index/index failure, checking too much data at one time, etc.), or the MySQL node has a high CPU usage rate or a high IO utilization rate, it will cause a SQL execution to fail. It is relatively slow; but sometimes, "only check one line of data" will also appear "slower";

This article gives an example of an SQL statement that may be locked and executed slowly when executing a "check a row" on a simple table, which involves the concepts of table locks, row locks, and consistent reading;

For the convenience of description, a table is constructed below to illustrate the problem based on this table; this table has two fields id and c, and I inserted (1,1), (2,2)..., (100000, 100000) these 100,000 rows of records;

CREATE TABLE `t` (
  `id` int(11) NOT NULL,
  `c` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

Phenomenon A: The SQL for checking a single record does not return for a long time after execution

Phenomenon description: Execute the following simple SQL statement, but the query result is not returned for a long time;

select * from t where id=1;

Generally, if you encounter this situation, there is a high probability that the table t is locked; when analyzing the reason, you usually first execute the show processlist command to see what state the current statement is in, and then analyze each state. why they arise, how to reproduce them, and what to do about them;

Case 1: Waiting for MDL lock

Metadata lock (metadata lock), referred to as MDL lock, is a table-level lock; MDL does not need to be used explicitly, and will be added automatically when accessing a table. Its role is to ensure the correctness of reading and writing;

When adding, deleting, modifying and querying a table, add an MDL read lock; when doing structural changes to the table, add an MDL write lock; read locks are not mutually exclusive, between read and write locks, and between write locks The time is mutually exclusive, used to ensure the safety of the operation of changing the table structure;

The simple steps to reproduce this SQL that does not return for a long time are given below;

Analysis: sessionA holds the MDL write lock of table t through the lock table command, and the query of sessionB needs to obtain the MDL read lock; therefore, sessionB enters the waiting state;

The way to deal with this kind of problem is to find who holds the MDL write lock, and then kill it; how to find this process ID? ——By querying the sys.schema_table_lock_waits table, you can directly find out the process id that caused the blockage, and disconnect the connection with the kill command;

Case 2: Waiting for the flush table

Next, introduce another situation where the query is blocked; on the table t, execute the following SQL statement to view the process list;

select * from information_schema.processlist;

It is found that the status of this thread is Waiting for table flush ;

Let’s first talk about the function of the flush table. Flush tables means closing all opened table objects and clearing the results in the query cache; that is to say, one effect of flush tables is to wait for the end of all running SQL requests ; because, Before the SQL statement is executed, the corresponding table object will be opened. For example, the select * fromt t statement will find the frm file of table t and open the table memory object; in order to control the memory space and other resources used by the table object, MySQL will implicitly (Background table object management thread) or explicitly (flush tables, etc.) to close the table object that has been opened but not in use; however, the table object in use cannot be closed (such as the SQL request is still running), therefore, flush Tables operations will be blocked by running SQL requests ;

Under normal circumstances, the flush tables statement is executed quickly unless it is also blocked by other threads; therefore, the possible situation of the Waiting for table flush state is: a flush tables command is blocked by other statements, and then It blocks our select statement again ;

To reproduce this situation, follow the steps below;

analyze:

(1) In sessionA, I intentionally call sleep(1) once for each row, so that in this table with 100,000 pieces of data, this statement will be executed for 100,000 seconds by default, during which table t is always opened by sessionA "Then;
(2) Then, if sessionB's flush tablest command is to close table t, it needs to wait for sessionA's query to end;
(3) In this way, if sessionC wants to query again, it will be blocked by the flush command;

The figure below is the show processlist result of this reproduction step;

The troubleshooting of this example is also very simple. When you see the result of the show processlist and find the thread ID of sessionA, you must kill the process of sessionA first, and then wait for the command flush tables to be executed, select * from t where id =1 command can be executed;

Case 3: waiting for row lock

Now, this statement has passed the test of table-level locks, and finally reaches the storage engine level; execute the following SQL statement:

select * from t where id=1 lock in share mode;

The usage of this statement means "current reading", which means that the latest data needs to be obtained, so a read lock (S lock, shared lock) is required; if a transaction is updating this data at this time, then this transaction holds this A write lock (X lock, exclusive lock) for row data, then this "current read" SQL needs to release the lock after the transaction is committed. Before the release, the select statement will be blocked waiting to acquire the lock;

To reproduce this situation, follow the steps below;

Analysis: sessionA starts a transaction, occupies a write lock, and has not committed yet, causing sessionB to be blocked;

This problem is not difficult to analyze, but the problem is how to find out who is occupying the write lock; if you are using MySQL5.7 version, you can find it through the sys.innodb_lock_waits table; the query method is:

mysql> select * from t sys.innodb_lock_waits where locked_table='`test`.`t`'\G

The result is as follows:

It can be seen that this information is very complete. The S lock with thread ID=4 blocks the subsequent SQL statement, and the row lock can be released through the KILL 4 command ; when the connection is disconnected, the executing thread in the connection will be automatically rolled back , which releases the row lock on the record with id=1 ;

Phenomenon B: The SQL execution for checking a single record takes a long time

After a lot of "locking", let's take a look at some examples of slow execution of queries;

Case 1: Full table scan

Execute the following SQL statement:

mysql> select * from t where c=50000 limit 1;

Since there is no index on field c, this statement can only be scanned in the order of the id primary key, and all records are not satisfied after checking, so 100,000 rows need to be scanned; as a confirmation, you can take a look at the slow query log;

Rows_examined shows that 50,000 rows have been scanned, and the execution time is 11ms; you may say that it is not very slow to return in 11.5ms, and online configurations generally exceed 1000ms to be considered slow queries; but you must remember: bad queries are not necessarily slow queries , when a certain condition changes, such as the amount of table data increases, the original bad query will be "revealed" ; in our example, there are only 100,000 rows of records, and if the amount of data increases, the execution time will increase linearly; The number of scan lines is large, so the execution is slow, which is easy to understand;

Case 2: A large number of updates generate a large number of undo logs

Execute the following SQL statement and view its slow log:

select * from t where id=1;

Phenomenon: Although the number of scan lines is 1, the execution time is as long as 800ms;

Look at the next SQL statement and his slow log:

select * from t where id=1 lock in share mode;

Phenomenon: The number of rows scanned during execution is also 1 row, and the execution time is 0.2 milliseconds; the lock in share mode plus read lock is used , but the actual execution time is much shorter than the query statement without lock ;

View the query results of the two statements, as follows, in the query result of the first statement, c=1, and the statement with lock in share mode returns c=1000001;

Analysis: It shows that the value of the latest version of data c read by "Current Read" is 1000001, and the version data c=1 of this line record in the first statement "Consistency View", indicating that there must be a pair of this record in the middle The update operation ;

To reproduce this situation, follow the steps below;

sessionA first starts a transaction with the start transaction with consistent snapshot command, and then sessionB executes 1 million update statements. At this time, the state of the row with id=1 is as follows;

SessionB has been updated 1 million times, and 1 million undologs have been generated; the SQL statement with lock in share mode is currently read, so it will directly read the result of 1000001 , so the speed is very fast ; and select * from t The statement whereid=1 is a consistent read, so it needs to start from 1000001, execute undolog in sequence, and only return the result of 1 after executing 1 million times, so it is slow ;

summary

This article introduces the SQL of "checking a row" on a simple table, and the execution may be slow;

(1) For the case of not returning for a long time, the reasons may be: waiting for MDL table lock, waiting for flush table (there are many SQLs still executing before flushing), "current read" and other row locks;

(2) For the case where the execution time of a simple query is much longer than the expected time, the reason may be: during the query, a large number of updates to the row record are performed in the uncommitted transaction, resulting in "consistent read" that needs to be executed from the current version multiple undolog;

Next article: "45 Lectures on MySQL Practical Combat" - Study Notes 20 "Phantom reading, locking method of full table scan, gap lock, next-key lock"

References for this chapter: 19 | Why is the execution of the statement that I only check one line so slow? - geek time

Guess you like

Origin blog.csdn.net/minghao0508/article/details/127923852