"select ... for update" exclusive lock in Mysql (transfer)

Original post address

https://blog.csdn.net/claram/article/details/54023216

Mysql InnoDB exclusive lock

用法: select … for update;

例如:select * from goods where id = 1 for update;

Prerequisites for applying for an exclusive lock: No thread uses an exclusive lock or a shared lock on any row data in the result set, otherwise the application will be blocked.

for update only works with InnoDB and must be in a transaction block (BEGIN/COMMIT) to take effect. During transaction operations, through the "for update" statement, MySQL will add an exclusive lock to each row of data in the query result set, and other threads will block the update and delete operations of the record. Exclusive locks include row locks and table locks.

Scenario Analysis

Suppose there is a commodity table goods, which contains three fields: id, commodity name, and inventory quantity. The table structure is as follows:

1 CREATE TABLE `goods` (
2   `id` int(11) NOT NULL AUTO_INCREMENT,
3   `name` varchar(100) DEFAULT NULL,
4   `stock` int(11) DEFAULT NULL,
5   PRIMARY KEY (`id`),
6   UNIQUE KEY `idx_name` (`name`) USING HASH
7 ) ENGINE=InnoDB 

Insert the following data:

1 INSERT INTO `goods` VALUES ('1', 'prod11', '1000');
2 INSERT INTO `goods` VALUES ('2', 'prod12', '1000');
3 INSERT INTO `goods` VALUES ('3', 'prod13', '1000');
4 INSERT INTO `goods` VALUES ('4', 'prod14', '1000');
5 INSERT INTO `goods` VALUES ('5', 'prod15', '1000');
6 INSERT INTO `goods` VALUES ('6', 'prod16', '1000');
7 INSERT INTO `goods` VALUES ('7', 'prod17', '1000');
8 INSERT INTO `goods` VALUES ('8', 'prod18', '1000');
9 INSERT INTO `goods` VALUES ('9', 'prod19', '1000');

1. Data Consistency

Suppose two users, A and B, buy a product with id=1 at the same time. The inventory obtained by user A is 1000, and the inventory obtained by user B is also 1000. After user A completes the purchase, modify the inventory of the product The quantity is 999. After user B completes the purchase, the stock quantity of the item is changed to 999. At this time, the stock quantity data is inconsistent.

There are two solutions:

Pessimistic lock scheme: every time you get an item, add an exclusive lock to the item. That is, when user A obtains the product information with id=1, the row record is locked, during which other users block waiting to access the record. Pessimistic locks are suitable for scenarios with frequent writes.

1 begin;
2 select * from goods where id = 1 for update;
3 update goods set stock = stock - 1 where id = 1;
4 commit;

Optimistic locking scheme: each time a product is acquired, the product is not locked. When updating data, it is necessary to compare whether the inventory in the program is equal to the inventory in the database. If they are equal, update the program. Otherwise, the program re-acquires the inventory and compares it again. The data is updated until the values ​​of the two inventory are equal. . Optimistic locking is suitable for scenarios with frequent reads.

1 #Get the commodity object with
 id=1 without locking 2 select * from goods where id = 1
 3  
4  begin;
 5 #Update the stock value, here you need to pay attention to the where condition "stock = cur_stock", only the inventory obtained in the program 6 update goods set stock = stock - 1 where id = 1 and stock = cur_stock
 ;
 7 commit;

If we need to design a mall system, which of the above solutions should we choose?

The frequency of inquiring products is higher than the frequency of placing orders and paying. Based on the above, I may give priority to the second solution (of course there are other solutions, only the above two solutions are considered here).

Second, row locks and table locks

1. Query only based on the primary key, and the data is queried, and the primary key field generates a row lock.

1 begin;
2 select * from goods where id = 1 for update;
3 commit;

2. Only query according to the primary key, no data is queried, and no lock is generated.

1 begin;
2 select * from goods where id = 1 for update;
3 commit;

3. Query according to the primary key, non-primary key with index (name), and query the data, the primary key field generates row lock, and the name field generates row lock.

1 begin;
2 select * from goods where id = 1 and name='prod11' for update;
3 commit;

4. Query according to the primary key, non-primary key including index (name), no data is queried, and no lock is generated.

1 begin;
2 select * from goods where id = 1 and name='prod12' for update;
3 commit;

5. Query according to the primary key, non-primary key without index (name), and query the data, if other threads query again according to the primary key field, the primary key field will generate a row lock, if other threads query according to the non-primary key without index fields , the non-primary key does not contain an index field to generate a table lock. If other threads query by a non-primary key containing an index field, the non-primary key contains an index field to generate a row lock. If the index value is an enumeration type, mysql will also perform a table lock. The paragraph is a bit awkward, so please understand it carefully.

1 begin;
2 select * from goods where id = 1 and name='prod11' for update;
3 commit;

6. Query according to the primary key, non-primary key without index (name), no data is queried, and no lock is generated.

1 begin;
2 select * from goods where id = 1 and name='prod12' for update;
3 commit;

7. Query according to the non-primary key including the index (name), and the data is queried, and the name field generates a row lock.

1 begin;
2 select * from goods where name='prod11' for update;
3 commit;

8. Query based on non-primary key including index (name), no data is queried, and no lock is generated.

1 begin;
2 select * from goods where name='prod11' for update;
3 commit;

9. Query according to the non-primary key without index (name), and the data is queried, and the name field generates a table lock.

1 begin;
2 select * from goods where name='prod11' for update;
3 commit;

10. Query according to the non-primary key without index (name), no data is queried, and the name field generates a table lock.

1 begin;
2 select * from goods where name='prod11' for update;
3 commit;

11. Query only based on the primary key, the query condition is not equal, and the data is queried, the primary key field generates a table lock.

1 begin;
2 select * from goods where id <> 1 for update;
3 commit;

12. Query only based on the primary key, the query condition is not equal, no data is queried, and the primary key field generates a table lock.

1 begin;
2 select * from goods where id <> 1 for update;
3 commit;

13. Query only based on the primary key, the query condition is like, and the data is queried, and the primary key field generates a table lock.

1 begin;
2 select * from goods where id like '1' for update;
3 commit;

14. Query only based on the primary key, the query condition is like, no data is queried, and the primary key field generates a table lock.

1 begin;
2 select * from goods where id like '1' for update;
3 commit;

test environment

Database version: 5.1.48-community

Database Engine: InnoDB Supports transactions, row-level locking, and foreign keys

Database isolation policy: REPEATABLE-READ (system, session)

Summarize

1. InnoDB row locks are implemented by locking the index items on the index. Only when data is retrieved through index conditions, InnoDB uses row-level locks. Otherwise, InnoDB uses table locks.

2. Since MySQL's row lock is a lock for an index, not a lock for a record, although records of different rows are accessed, if the same index key is used, there will be lock conflicts. Keep this in mind when designing your application. 
3. When the table has multiple indexes, different transactions can use different indexes to lock different rows. In addition, whether using primary key indexes, unique indexes or ordinary indexes, InnoDB will use row locks to lock data. 
4. Even if the index field is used in the condition, whether to use the index to retrieve data is determined by MySQL by judging the cost of different execution plans. If MySQL thinks that the full table scan is more efficient, for example, for some small tables, It will not use the index, in which case InnoDB will use table locks, not row locks. Therefore, when analyzing lock conflicts, don't forget to check the SQL execution plan to confirm whether the index is actually used. 
5. The data type of the retrieved value is different from the index field. Although MySQL can perform data type conversion, it does not use the index, which causes InnoDB to use table locks. We can see this clearly by examining the execution plans of the two SQLs with explain.

 

 

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325251123&siteId=291194637