How to solve the deadlock caused by MySQL optimization index merge
Background
A deadlock occurred in the production environment. By checking the deadlock log, I saw that the deadlock was caused by two identical update statements (only the values in the where condition were different),
is as follows:
UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx1' AND `status` = 0; UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx2' AND `status` = 0;
It was difficult to understand at first. After a lot of inquiry and study, I analyzed the specific principles of deadlock formation. I would like to share it with everyone in the hope that it can help anyone who encounters it. Friends with the same problem.
Because there are many knowledge points in MySQL
, many nouns will not be introduced too much here. Friends who are interested can follow up with special in-depth study.
Deadlock log
*** (1) TRANSACTION: TRANSACTION 791913819, ACTIVE 0 sec starting index read, thread declared inside InnoDB 4999 mysql tables in use 3, locked 3 LOCK WAIT 4 lock struct(s), heap size 1184, 3 row lock(s) MySQL thread id 462005230, OS thread handle 0x7f55d5da3700, query id 2621313306 x.x.x.x test_user Searching rows for update UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx1' AND `status` = 0; *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 110 page no 39167 n bits 1056 index `idx_status` of table `test`.`test_table` trx id 791913819 lock_mode X waiting Record lock, heap no 495 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 *** (2) TRANSACTION: TRANSACTION 791913818, ACTIVE 0 sec starting index read, thread declared inside InnoDB 4999 mysql tables in use 3, locked 3 5 lock struct(s), heap size 1184, 4 row lock(s) MySQL thread id 462005231, OS thread handle 0x7f55cee63700, query id 2621313305 x.x.x.x test_user Searching rows for update UPDATE test_table SET `status` = 1 WHERE `trans_id` = 'xxx2' AND `status` = 0; *** (2) HOLDS THE LOCK(S): RECORD LOCKS space id 110 page no 39167 n bits 1056 index `idx_status` of table `test`.`test_table` trx id 791913818 lock_mode X Record lock, heap no 495 PHYSICAL RECORD: n_fields 2; compact format; info bits 0 *** (2) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 110 page no 41569 n bits 88 index `PRIMARY` of table `test`.`test_table` trx id 791913818 lock_mode X locks rec but not gap waiting Record lock, heap no 14 PHYSICAL RECORD: n_fields 30; compact format; info bits 0 *** WE ROLL BACK TRANSACTION (1)
Briefly analyze the above deadlock log:
1. The first content ( Lines 1 to 9), line 6 is the SQL statement executed by transaction (1), lines 7 and 8 mean that transaction (1) is waiting for the X lock on the idx_status index;
2. In the second block of content (line 11 to line 19), line 16 is the SQL statement executed by transaction (2), and lines 17 and 18 mean that transaction (2) holds the idx_status index The X lock on;
means: Transaction (2) is waiting to acquire the X lock on the PRIMARY index. (but not gap means not a gap lock)
4. The last sentence means that MySQL rolled back transaction (1).
Table structure
CREATE TABLE `test_table` ( `id` int(11) NOT NULL AUTO_INCREMENT, `trans_id` varchar(21) NOT NULL, `status` int(11) NOT NULL, PRIMARY KEY (`id`), UNIQUE KEY `uniq_trans_id` (`trans_id`) USING BTREE, KEY `idx_status` (`status`) USING BTREE ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
As can be seen from the table structure, there is a unique index on the trans_id
columnuniq_trans_id
,status
There is a normal index on the column idx_status
, and the id column is the primary key index PRIMARY
.
There are two kinds of indexes in the InnoDB engine:
Clustered index: Put the data storage and index together , the leaf nodes of the index structure store row data.
Auxiliary index: The auxiliary index leaf node stores the primary key value, which is the key value of the clustered index.
Primary key index PRIMARY
is a clustered index, and row data will be stored in leaf nodes. uniq_trans_id
Index and idx_status
The index is an auxiliary index, and the leaf node stores the primary key value, which is the id column value.
When we search for row data through the auxiliary index, we first find the primary key id through the auxiliary index, then perform a secondary search through the primary key index (also called back to the table), and finally find the row data.
Execution plan
By looking at the execution plan, you can find that the update statement uses index merging, that is, this statement uses both uniq_trans_id
Index is used again idx_status
Index, Using intersect(uniq_trans_id,idx_status)
means to obtain the intersection through two indexes.
Why index_merge is used
Before MySQL5.0, a table could only use one index at a time, and multiple indexes could not be used at the same time for conditional scans. But starting from 5.1, index merge
optimization technology has been introduced, and multiple indexes can be used to perform conditional scans on the same table.
For example, the statement in the execution plan:
UPDATE test_table SET `status` = 1 WHERE `trans_id` = '38' AND `status` = 0 ;
MySQL will use uniq_trans_id# based on the condition
trans_id = ‘38’ ## The index finds the id value saved in the leaf node; at the same time, according to the condition
status = 0, the
idx_status index is used to find the id value saved in the leaf node; then the two found The group ID values are intersected, and finally the intersection ID is returned to the table, that is, the row data saved in the leaf node is found through the PRIMARY index.
uniq_trans_id is already a unique index. Only one piece of data can be found at most through this index. Then why does the MySQL optimizer use two Take the intersection of two indexes and then return to the table for query. Doesn't this add another
idx_status index search process? Let's analyze the execution process of these two situations.
The first one only uses uniq_trans_id index:
- ##According to the query condition of
- trans_id = ‘38’
, use
uniq_trans_id
Index finds the id value saved in the leaf node; Use the PRIMARY index to find the row data saved in the leaf node through the found id value; - Then filter the found row data through the
- status = 0
condition.
The second type uses index merging
:
- According to
- trans_id = ‘38’
Query conditions, use
uniq_trans_id
index to find the id value saved in the leaf node; According to - status = 0
Query conditions, use
idx_status
index to find the id value saved in the leaf node; Take the intersection of the id values found in 1/2, and then use the PRIMARY index to find the row data saved in the leaf node
In the above two cases, The main difference is that the first method is to find the data through an index first, and then use other query conditions to filter; the second method is to first obtain the intersection of the id values found through the two indexes. If there is still an id value after the intersection, , then go back to the table to get the data.
When the optimizer believes that the execution cost of the second case is smaller than that of the first case, index merging will occur. (There is very little data in the production environment flow table with status = 0
, which is one of the reasons why the optimizer considers the second case).
Why is it deadlocked after using index_merge
The above briefly draws the process of locking two update transactions. As can be seen in the figure, there are overlapping and intersecting parts on both the idx_status
index and PRIMARY (clustered index), which creates conditions for deadlock.
For example, a deadlock will occur when the following timing is encountered:
Transaction 1 waits for transaction 2 to release the lock, Transaction 2 waits for transaction 1 to release the lock, thus causing a deadlock.
After MySQL detects a deadlock, it will automatically roll back the transaction with the lower cost. As in the timing diagram above, if transaction 1 holds fewer locks than transaction 2, then MySQL will proceed with transaction 1. rollback.
Solution
1. From the code level
#where query conditions, only pass
trans_id
to query the data Finally, determine whether the status is 0 at the code level;Use
force index(uniq_trans_id)
Force the query statement to use theuniq_trans_id
index;where use the id field directly after the query condition and update it through the primary key.
2. Delete the
idx_status- index from the MySQL level
or create a joint index containing these two columns;
Turn off the
index merge
optimization of the MySQL optimizer.
The above is the detailed content of How to solve the deadlock caused by MySQL optimization index merge. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics

The main role of MySQL in web applications is to store and manage data. 1.MySQL efficiently processes user information, product catalogs, transaction records and other data. 2. Through SQL query, developers can extract information from the database to generate dynamic content. 3.MySQL works based on the client-server model to ensure acceptable query speed.

The process of starting MySQL in Docker consists of the following steps: Pull the MySQL image to create and start the container, set the root user password, and map the port verification connection Create the database and the user grants all permissions to the database

Laravel is a PHP framework for easy building of web applications. It provides a range of powerful features including: Installation: Install the Laravel CLI globally with Composer and create applications in the project directory. Routing: Define the relationship between the URL and the handler in routes/web.php. View: Create a view in resources/views to render the application's interface. Database Integration: Provides out-of-the-box integration with databases such as MySQL and uses migration to create and modify tables. Model and Controller: The model represents the database entity and the controller processes HTTP requests.

I encountered a tricky problem when developing a small application: the need to quickly integrate a lightweight database operation library. After trying multiple libraries, I found that they either have too much functionality or are not very compatible. Eventually, I found minii/db, a simplified version based on Yii2 that solved my problem perfectly.

The key to installing MySQL elegantly is to add the official MySQL repository. The specific steps are as follows: Download the MySQL official GPG key to prevent phishing attacks. Add MySQL repository file: rpm -Uvh https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm Update yum repository cache: yum update installation MySQL: yum install mysql-server startup MySQL service: systemctl start mysqld set up booting

Installing MySQL on CentOS involves the following steps: Adding the appropriate MySQL yum source. Execute the yum install mysql-server command to install the MySQL server. Use the mysql_secure_installation command to make security settings, such as setting the root user password. Customize the MySQL configuration file as needed. Tune MySQL parameters and optimize databases for performance.

Article summary: This article provides detailed step-by-step instructions to guide readers on how to easily install the Laravel framework. Laravel is a powerful PHP framework that speeds up the development process of web applications. This tutorial covers the installation process from system requirements to configuring databases and setting up routing. By following these steps, readers can quickly and efficiently lay a solid foundation for their Laravel project.

MySQL and phpMyAdmin are powerful database management tools. 1) MySQL is used to create databases and tables, and to execute DML and SQL queries. 2) phpMyAdmin provides an intuitive interface for database management, table structure management, data operations and user permission management.
