Optimistic locking and pessimistic locking - solving concurrency problems

 

introduction
Why do you need locks (concurrency control)

  In a multi-user environment, multiple users may be updating the same records at the same time, which can create conflicts. This is the famous concurrency problem.

Typical conflicts are:

  • Lost update : The update of one transaction overwrites the update result of other transactions, which is the so-called lost update. For example: User A changes the value from 6 to 2, and User B changes the value from 2 to 6, then User A loses his update.
  • Dirty Reads : Dirty reads occur when a transaction reads other records that are halfway through the transaction. For example, the value seen by user A and B is 6, user B changes the value to 2, and the value read by user A is still 6.

In order to solve the problems caused by these concurrency. We need to introduce concurrency control mechanism.

Concurrency Control Mechanism

  Pessimistic locking: Assuming that concurrency conflicts will occur, block all operations that may violate data integrity. [1]

  Optimistic locking: Assuming that no concurrency conflicts will occur , only checks for data integrity violations when committing an operation. [1] Optimistic locking cannot solve the problem of dirty reads.

 

optimistic locking application

Optimistic lock introduction:

  Optimistic Locking Compared with pessimistic locking , optimistic locking assumes that data will not cause conflict in general, so when the data is submitted and updated, the data conflict will be formally detected. If a conflict is found , then let the user return the wrong information, let the user decide what to do. So how do we implement optimistic locking? Generally speaking, there are two ways:

  1. Implemented using the data version (Version) recording mechanism, which is the most commonly used implementation of optimistic locking. What is a data version? That is, adding a version identifier to the data is generally realized by adding a numeric "version" field to the database table. When reading data, read the value of the version field together, and add one to the version value every time the data is updated. When we submit the update, compare the current version information of the corresponding record in the database table with the version value taken out for the first time. If the current version number of the database table is equal to the version value taken out for the first time, it will be updated. Otherwise, it is considered to be expired data. Use the following picture to illustrate:

As shown in the figure above, if the update operations are performed sequentially, the version of the data will be incremented in sequence, and there will be no conflict. However, if there are different business operations that modify the same version of the 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 the update operation of A will fail.

 

2. The second implementation method of optimistic locking is similar to the first one. It also adds a field to the table that needs optimistic locking control. The name does not matter. The field type uses timestamp , which is similar to the above version. When the update is submitted, check the timestamp of the data in the current database and compare it with the timestamp obtained before the update. If it is consistent, it is OK, otherwise it is a version conflict.

 

Example of use : Take MySQL InnoDB as an example

Take the previous example as an example: there is a field status in the goods table. If the status is 1, it means that the product has not been ordered. If the status is 2, it means that the product has been ordered. Then we must ensure that the product has been ordered when we place an order for the product. status is 1. Suppose the id of the item is 1.

 

The ordering operation includes 3 steps:

1. Check out the 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, add a version field, and the default version value of the data is 1.

  The initial data of the t_goods table is as follows:

copy code
mysql> select * from t_goods;
+----+--------+------+---------+
| id | status | name | version |
+----+--------+------+---------+
| 1 | 1 | Props | 1 |
| 2 | 2 | Equipment | 2 |
+----+--------+------+---------+
2 rows in set

mysql>
copy code

 

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

Goods entity class:

copy code
/**
 * ClassName: Goods <br/>
 * Function: Commodity entity. <br/>*/
public class Goods implements Serializable {

    /**
     * serialVersionUID: Serialization ID.
     */
    private static final long serialVersionUID = 6803791908148880587L;
    
    /**
     * id: primary key id.
     */
    private int id;
    
    /**
     * status: Product status: 1 has not placed an order, 2 has placed an order.
     */
    private int status;
    
    /**
     * name: product name.
     */
    private String name;
    
    /**
     * version: product data version number.
     */
    private int version;
    
    @Override
    public String toString(){
        return "good id:"+id+",goods status:"+status+",goods name:"+name+",goods version:"+version;
    }

    //setter and getter

}
copy code

 

GoodsDao

copy code
/**
 * updateGoodsUseCAS: Use CAS (Compare and set) to update product information

 * @param goods commodity object
 * The number of lines affected by @return
 */
int updateGoodsUseCAS(Goods goods);
copy code

 

mapper.xml

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

 

 

GoodsDaoTest test class

copy code
@Test
public void goodsDaoTest(){
    int goodsId = 1;
    //Query the product information according to the same id and assign it to 2 objects
    Goods goods1 = this.goodsDao.getGoodsById(goodsId);
    Goods goods2 = this.goodsDao.getGoodsById(goodsId);
    
    //Print the current product information
    System.out.println(goods1);
    System.out.println(goods2);
    
    //Update product information 1
    goods1.setStatus(2);//Modify status to 2
    int updateResult1 = this.goodsDao.updateGoodsUseCAS(goods1);
    System.out.println("Modify product information 1"+(updateResult1==1?"Success":"Failure"));
    
    //Update product information 2
    goods1.setStatus(2);//Modify status to 2
    int updateResult2 = this.goodsDao.updateGoodsUseCAS(goods1);
    System.out.println("Modify product information 2"+(updateResult2==1?"Success":"Failure"));
}
copy code

 

Output result:

good id:1,goods status:1,goods name:道具,goods version:1  
good id:1,goods status:1,goods name:道具,goods version:1  
Modification of product information 1 succeeded  
Failed to modify product information 2

 

 

illustrate:

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

copy code
mysql> select * from t_goods;
+----+--------+------+---------+
| id | status | name | version |
+----+--------+------+---------+
| 1 | 2 | Props | 2 |
| 2 | 2 | Equipment | 2 |
+----+--------+------+---------+
2 rows in set

mysql>
copy code

 

  We can see that the data version with id 1 has been modified to 2 during the first update. So 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

 

Pessimistic Lock Application

Need to use the database lock mechanism, such as SQL SERVER's TABLOCKX (exclusive table lock) When this option is selected, SQL Server will set an exclusive lock on the entire table until the end of the command or transaction. This will prevent other processes from reading or modifying the data in the table.

Used in SqlServer

Begin Tran
select top 1 @TrainNo=T_NO
         from Train_ticket   with (UPDLOCK)   where S_Flag=0

      update Train_ticket
         set T_Name=user,
             T_Time=getdate(),
             S_Flag=1
         where T_NO=@TrainNo
commit

We use the with (UPDLOCK) option when querying. When querying records, we add an update lock to the record, indicating that we are about to update this record. Note that update locks and shared locks do not conflict, that is Other users can also query the contents of this table, but it conflicts with update locks and exclusive locks. So other update users will block.

in conclusion

  In the actual production environment, if the amount of concurrency is not large and dirty reading is not allowed, pessimistic locking can be used to solve the concurrency problem; but if the concurrency of the system is very large, pessimistic locking will bring very big performance problems, so we have to choose A method for optimistic locking.

introduction
Why do you need locks (concurrency control)

  In a multi-user environment, multiple users may be updating the same records at the same time, which can create conflicts. This is the famous concurrency problem.

Typical conflicts are:

  • Lost update : The update of one transaction overwrites the update result of other transactions, which is the so-called lost update. For example: User A changes the value from 6 to 2, and User B changes the value from 2 to 6, then User A loses his update.
  • Dirty Reads : Dirty reads occur when a transaction reads other records that are halfway through the transaction. For example, the value seen by user A and B is 6, user B changes the value to 2, and the value read by user A is still 6.

In order to solve the problems caused by these concurrency. We need to introduce concurrency control mechanism.

Concurrency Control Mechanism

  Pessimistic locking: Assuming that concurrency conflicts will occur, block all operations that may violate data integrity. [1]

  Optimistic locking: Assuming that no concurrency conflicts will occur , only checks for data integrity violations when committing an operation. [1] Optimistic locking cannot solve the problem of dirty reads.

 

optimistic locking application

Optimistic lock introduction:

  Optimistic Locking Compared with pessimistic locking , optimistic locking assumes that data will not cause conflict in general, so when the data is submitted and updated, the data conflict will be formally detected. If a conflict is found , then let the user return the wrong information, let the user decide what to do. So how do we implement optimistic locking? Generally speaking, there are two ways:

  1. Implemented using the data version (Version) recording mechanism, which is the most commonly used implementation of optimistic locking. What is a data version? That is, adding a version identifier to the data is generally realized by adding a numeric "version" field to the database table. When reading data, read the value of the version field together, and add one to the version value every time the data is updated. When we submit the update, compare the current version information of the corresponding record in the database table with the version value taken out for the first time. If the current version number of the database table is equal to the version value taken out for the first time, it will be updated. Otherwise, it is considered to be expired data. Use the following picture to illustrate:

As shown in the figure above, if the update operations are performed sequentially, the version of the data will be incremented in sequence, and there will be no conflict. However, if there are different business operations that modify the same version of the 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 the update operation of A will fail.

 

2. The second implementation method of optimistic locking is similar to the first one. It also adds a field to the table that needs optimistic locking control. The name does not matter. The field type uses timestamp , which is similar to the above version. When the update is submitted, check the timestamp of the data in the current database and compare it with the timestamp obtained before the update. If it is consistent, it is OK, otherwise it is a version conflict.

 

Example of use : Take MySQL InnoDB as an example

Take the previous example as an example: there is a field status in the goods table. If the status is 1, it means that the product has not been ordered. If the status is 2, it means that the product has been ordered. Then we must ensure that the product has been ordered when we place an order for the product. status is 1. Suppose the id of the item is 1.

 

The ordering operation includes 3 steps:

1. Check out the 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, add a version field, and the default version value of the data is 1.

  The initial data of the t_goods table is as follows:

copy code
mysql> select * from t_goods;
+----+--------+------+---------+
| id | status | name | version |
+----+--------+------+---------+
| 1 | 1 | Props | 1 |
| 2 | 2 | Equipment | 2 |
+----+--------+------+---------+
2 rows in set

mysql>
copy code

 

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

Goods entity class:

copy code
/**
 * ClassName: Goods <br/>
 * Function: Commodity entity. <br/>*/
public class Goods implements Serializable {

    /**
     * serialVersionUID: Serialization ID.
     */
    private static final long serialVersionUID = 6803791908148880587L;
    
    /**
     * id: primary key id.
     */
    private int id;
    
    /**
     * status: Product status: 1 has not placed an order, 2 has placed an order.
     */
    private int status;
    
    /**
     * name: product name.
     */
    private String name;
    
    /**
     * version: product data version number.
     */
    private int version;
    
    @Override
    public String toString(){
        return "good id:"+id+",goods status:"+status+",goods name:"+name+",goods version:"+version;
    }

    //setter and getter

}
copy code

 

GoodsDao

copy code
/**
 * updateGoodsUseCAS: Use CAS (Compare and set) to update product information

 * @param goods commodity object
 * The number of lines affected by @return
 */
int updateGoodsUseCAS(Goods goods);
copy code

 

mapper.xml

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

 

 

GoodsDaoTest test class

copy code
@Test
public void goodsDaoTest(){
    int goodsId = 1;
    //Query the product information according to the same id and assign it to 2 objects
    Goods goods1 = this.goodsDao.getGoodsById(goodsId);
    Goods goods2 = this.goodsDao.getGoodsById(goodsId);
    
    //Print the current product information
    System.out.println(goods1);
    System.out.println(goods2);
    
    //Update product information 1
    goods1.setStatus(2);//Modify status to 2
    int updateResult1 = this.goodsDao.updateGoodsUseCAS(goods1);
    System.out.println("Modify product information 1"+(updateResult1==1?"Success":"Failure"));
    
    //Update product information 2
    goods1.setStatus(2);//Modify status to 2
    int updateResult2 = this.goodsDao.updateGoodsUseCAS(goods1);
    System.out.println("Modify product information 2"+(updateResult2==1?"Success":"Failure"));
}
copy code

 

Output result:

good id:1,goods status:1,goods name:道具,goods version:1  
good id:1,goods status:1,goods name:道具,goods version:1  
Modification of product information 1 succeeded  
Failed to modify product information 2

 

 

illustrate:

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

copy code
mysql> select * from t_goods;
+----+--------+------+---------+
| id | status | name | version |
+----+--------+------+---------+
| 1 | 2 | Props | 2 |
| 2 | 2 | Equipment | 2 |
+----+--------+------+---------+
2 rows in set

mysql>
copy code

 

  We can see that the data version with id 1 has been modified to 2 during the first update. So 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

 

Pessimistic Lock Application

Need to use the database lock mechanism, such as SQL SERVER's TABLOCKX (exclusive table lock) When this option is selected, SQL Server will set an exclusive lock on the entire table until the end of the command or transaction. This will prevent other processes from reading or modifying the data in the table.

Used in SqlServer

Begin Tran
select top 1 @TrainNo=T_NO
         from Train_ticket   with (UPDLOCK)   where S_Flag=0

      update Train_ticket
         set T_Name=user,
             T_Time=getdate(),
             S_Flag=1
         where T_NO=@TrainNo
commit

We use the with (UPDLOCK) option when querying. When querying records, we add an update lock to the record, indicating that we are about to update this record. Note that update locks and shared locks do not conflict, that is Other users can also query the contents of this table, but it conflicts with update locks and exclusive locks. So other update users will block.

in conclusion

  In the actual production environment, if the amount of concurrency is not large and dirty reading is not allowed, pessimistic locking can be used to solve the concurrency problem; but if the concurrency of the system is very large, pessimistic locking will bring very big performance problems, so we have to choose A method for optimistic locking.

Guess you like

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