[MySQl] How is optimistic locking in MySQl implemented?

foreword

How is optimistic locking implemented in mysql? Many novices are not very clear about this. In order to help you solve this problem, I will explain it in detail below. Those who have this need can come and learn. I hope you can gain something.

1. Optimistic lock

Optimistic Locking (Optimistic Locking) Compared with pessimistic locking, the optimistic locking mechanism adopts a more relaxed locking mechanism. In most cases, pessimistic locking is implemented by the locking mechanism of the database to ensure the maximum degree of exclusivity of the operation. But what comes with it is a lot of overhead in database performance, especially for long transactions, such overhead is often unbearable.

The optimistic locking mechanism solves this problem to a certain extent. Optimistic locking is mostly implemented based on the data version (Version) recording mechanism. What is a data version? That is to add a version identifier to the data. In the version solution based on the database table, it is generally realized by adding a "version" field to the database table. When reading out the data, read this version number together, and when updating later, add one to this version number.

At this time, the version data of the submitted data is compared with the current version information of the corresponding record in the database table. If the version number of the submitted data is greater than the current version number of the database table, it is updated, otherwise it is considered as expired data.

advantage:

As can be seen from the above example, the optimistic locking mechanism avoids the database locking overhead in long transactions (operator A and operator B do not lock the database data during the operation process), which greatly improves the overall performance of the system under large concurrency.

shortcoming:

It should be noted that the optimistic locking mechanism is often based on the data storage logic in the system, so it also has certain limitations. For example, in the above example, since the optimistic locking mechanism is implemented in our system, user balance update operations from external systems are not controlled by our system, so dirty data may be updated to the database. In the system design phase, we should fully consider the possibility of these situations and make corresponding adjustments (such as implementing the optimistic locking strategy in the database storage procedure, and only open the data update method based on this storage procedure to the outside world, instead of directly exposing the database table to the outside world).

2. How to implement optimistic locking? Generally speaking, there are two ways

2.1, using the data version (Version) recording mechanism to achieve

This is the most commonly used implementation of optimistic locking. What is a data version? That is to add a version identifier to the data, usually by adding a numeric "version" field to the database table. When reading data, read the value of the version field together. Every time the data is updated, the version value is incremented by one.

When we submit an update, we judge that the current version information of the corresponding record in the database table is compared with the version value extracted for the first time. If the current version number of the database table is equal to the version value extracted for the first time, it is updated, otherwise it is considered as expired data. Use the following picture to illustrate:

How to implement optimistic locking in mysql

As shown in the figure above, if the update operations are performed sequentially, the version of the data will be incremented sequentially, and no conflicts will occur. However, if different business operations modify the same version of data, the operation submitted first (B in the figure) will update the data version to 2. When A submits the update after B, it finds that the version of the data has been modified, then A's update operation will fail.

2.2. The second implementation of optimistic locking is similar to the first one

The same is to add a field to the table that requires optimistic lock control. The name does not matter. The field type uses timestamp (timestamp), which is similar to the version above. When the update is submitted, the timestamp of the data in the current database is checked and compared with the timestamp obtained before the update. If they are consistent, it is OK, otherwise it is a version conflict.

Example of use:

Take MySQL InnoDB as an example

Let’s take the previous example as an example: there is a status field in the goods table, a status of 1 means that the product has not been ordered, and a status of 2 means that the product has been ordered, so when we place an order for a certain product, we must ensure that the product’s status is 1. Suppose the product id is 1.

Placing an order includes 3 steps:

1. Query product information

select (status,status,version) from t_goods where id=#{id}

2. Generate orders based on product information

3. Modify the product status to 2

update t_goods 
set status=2,version=version+1
where id=#{id} and version=#{version};

Then in order to use optimistic locking, we first modify the t_goods table and add a version field. The default version value of the data is 1.

The initial data of the t_goods table is as follows:

mysql> select * from t_goods;  
+----+--------+------+---------+  
| id | status | name | version |  
+----+--------+------+---------+  
|  1 |      1 | 道具 |       1 |  
|  2 |      2 | 装备 |       2 |  
+----+--------+------+---------+  
2 rows in set  
  
mysql>

For the implementation of optimistic locking, I use MyBatis for practice, as follows:

Goods entity class:

public class Goods implements Serializable {
    
      
  
    /** 
     * serialVersionUID:序列化ID. 
     */  
    private static final long serialVersionUID = 6803791908148880587L;  
      
    /** 
     * id:主键id. 
     */  
    private int id;  
      
    /** 
     * status:商品状态:1未下单、2已下单. 
     */  
    private int status;  
      
    /** 
     * name:商品名称. 
     */  
    private String name;  
      
    /** 
     * version:商品数据版本号. 
     */  
    private int version;  
      
    @Override  
    public String toString(){
    
      
        return "good id:"+id+",goods status:"+status+",goods name:"+name+",goods version:"+version;  
    }  
  
    //setter and getter  
  
}

GoodsDao

/** 
 * updateGoodsUseCAS:使用CAS(Compare and set)更新商品信息. <br/> 
 * 
 */  
int updateGoodsUseCAS(Goods goods);
mapper.xml

<update id="updateGoodsUseCAS" parameterType="Goods">  
    <![CDATA[ 
        update t_goods 
        set status=#{
    
    status},name=#{
    
    name},version=version+1 
        where id=#{
    
    id} and version=#{
    
    version} 
    ]]>  
</update>

GoodsDaoTest test class

@Test  
public void goodsDaoTest(){
    
      
    int goodsId = 1;  
    //根据相同的id查询出商品信息,赋给2个对象  
    Goods goods1 = this.goodsDao.getGoodsById(goodsId);  
    Goods goods2 = this.goodsDao.getGoodsById(goodsId);  
      
    //打印当前商品信息  
    System.out.println(goods1);  
    System.out.println(goods2);  
      
    //更新商品信息1  
    goods1.setStatus(2);//修改status为2  
    int updateResult1 = this.goodsDao.updateGoodsUseCAS(goods1);  
    System.out.println("修改商品信息1"+(updateResult1==1?"成功":"失败"));  
      
    //更新商品信息2  
    goods1.setStatus(2);//修改status为2  
    int updateResult2 = this.goodsDao.updateGoodsUseCAS(goods1);  
    System.out.println("修改商品信息2"+(updateResult2==1?"成功":"失败"));  
}

Output result:

good id:1,goods status:1,goods name:道具,goods version:1  
good id:1,goods status:1,goods name:道具,goods version:1  
修改商品信息1成功  
修改商品信息2失败

illustrate:

In the GoodsDaoTest test method, we find out the same version of data at the same time, assign them to different goods objects, and then modify the good1 object first and then perform the update operation, and the execution is successful. Then we modify goods2, and when the update operation is performed, it prompts that the operation failed. At this time, the data in the t_goods table is as follows:

mysql> select * from t_goods;  
+----+--------+------+---------+  
| id | status | name | version |  
+----+--------+------+---------+  
|  1 |      2 | 道具 |       2 |  
|  2 |      2 | 装备 |       2 |  
+----+--------+------+---------+  
2 rows in set  
  
mysql>

We can see that the data version with id 1 has been changed to 2 in the first update. Therefore, when we update good2, the update where condition does not match, so the update will not succeed. The specific sql is as follows:

update t_goods   
set status=2,version=version+1  
where id=#{id} and version=#{version};

In this way we achieve optimistic locking.

Guess you like

Origin blog.csdn.net/u011397981/article/details/131860683