JPA uses optimistic locking to deal with high concurrency

  • 2021-11-29 07:20:30
  • OfStack

Directory JPA uses optimistic locks to meet the challenges of high concurrency and high concurrency systems. Pessimistic locks are a good thing to add optimistic locks to databases. Optimistic locks-business judgment to solve high concurrency

JPA uses optimistic locking to cope with high concurrency

Challenges of highly concurrent systems

When deploying distributed systems, we usually deploy multiple microservices in intranet clusters, and then aggregate them with API gateway to provide them to the outside world. In order to do load balancing, multiple running instances are usually started for each microservice and called through the registry.

Then the problem comes, because there are multiple instances running the same application. Although the micro-service gateway will only forward every request to one instance, when faced with high concurrency, they may still operate the same database table at the same time. Will this cause any problems?

The Problem of Pessimistic Lock

For example, in the common commodity spike system in e-commerce, there will be a large number of concurrent requests in the process of snapping up commodities, and it is very likely to read and write a table containing the remaining quantity of commodities at the same time. This kind of one should lock the database, otherwise it is easy to oversell and missell commodities.

If you use the locking mechanism that comes with the database, That is, pessimistic lock, which locks the database when writing, and must wait for the lock to be released when other modification requests arrive, but this reduces the efficiency. Moreover, in the high concurrency scenario, some requests 1 may not grab the lock directly, and they will get stuck there for a long time, resulting in the request failure, and then there will be a large number of retries, and the system may have anomalies such as exhaustion of connections.

Optimistic lock is a good thing

Looking up the data, I found that optimistic lock is a good thing. It is realized by adding an version field to the database table to identify the data version. When reading data, read version field 1 together. When writing to the database, compare version field to know whether the data has been changed. If version is not equal, it means that it holds expired data and cannot be written. If it is equal, it can be written, and version is added by 1.

Optimistic lock will check whether the data conflicts when it is written to the database. If the conflict is found, it will give up writing and return the information of writing failure. Compared with pessimistic lock, this is a lightweight locking method for data, which can cope with high concurrency requirements.

Add optimistic locks to the database

To say that optimistic lock is a good thing, first of all, we must say that JPA is a good thing, because Spring Data JPA has built-in implementation of optimistic lock. It is very simple to add optimistic lock to database table. It is enough to add an integer field and add @ Version annotation. JPA will automatically check the version every time data is submitted.


    @Entity  
    @Table(name = "m_order")  
    public class Order {  
    ...  
        @Version  
        private int version;  
    ...  
    }  

Optimistic lock-business judgment to solve high concurrency

In order to solve the problem of high concurrency, if it is distributed system, obviously we can only use database-side locking mechanism to solve this problem, but this synchronization mechanism or database physical locking mechanism will sacrifice one part of the performance, so we often solve this problem in another way: optimistic locking mode

It is a typical optimistic lock mode for two bank operators to operate the same account at the same time.

For example, A and B operators read the account with a balance of 1000 yuan at the same time, A operators add 100 yuan to the account, B operators deduct 50 yuan for the account at the same time, A submits first, and B submits later. The final actual account balance is 1000-50=950 yuan, but it should have been 1000+100-50=1050. This is a typical concurrency problem.

Optimistic locking mechanism solves this problem to a certain extent. Optimistic locks are mostly implemented based on data version (Version) recording mechanism. What is a data version? That is, add a version identification to the data. In the version solution based on database tables, 1 is generally realized by adding a "version" field to the database tables.

When the data is read out, the version number 1 is read out together, and when the data is updated later, the version number is added with 1. At this time, the version data of the submitted data is compared with the current version information of the corresponding record of 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.

For the above example of modifying user account information, assume that there is an version field in the account information table in the database, and the current value is 1; The current account balance field (balance) is 1000 yuan. Assume that operator A is updated first and operator B is updated later.

a, operator A now reads it out (version=1) and adds 100 (1000 +100 = 1100) from his account balance. b, during the operation of Operator A, Operator B also reads this user information (version=1) and deducts 50 from his account balance (1000-50 = 950). c, operator A completed the modification work, adding 1 to the data version number (version=2), together with the balance after account increase (balance=1100), and submitting it to the database for update. At this time, because the submitted data version is larger than the current version of the database record, the data is updated, and the database record version is updated to 2. d, operator B completed the operation, The version number is also added by 1 (version=2) to try to submit data to the database (balance=950). However, when comparing the database record versions, it is found that the data version number submitted by the operator B is 2, and the current version of the database record is also 2, which does not meet the optimistic lock strategy of "the submitted version must be greater than the current version of the record before the update can be performed". Therefore, the submission of the operator B is rejected. Thus, the possibility that the operator B overwrites the operation result of the operator A with the result modified by the old data based on version=1 is avoided.

Operator A operates as follows:


select id, balance, version from account where id="1"; 
 Query results: id=1, balance=1000, version=1
update account 
set balance=balance+100, version=version+1 
where id="1" and version=1
select id, balance, version from account where id="1"; 
 Query results: id=1, balance=1100, version=2

Operator B operates as follows:


select id, balance, version from account where id="1"; 
 Query results: id=1, balance=1000, version=1
# Operator A Modified successfully, actual account.balance=1100 , account.version=2 , operator B Also add the version number 1(version=2) Attempt to submit data to database (balance=950) However, when comparing the database record versions, it is found that the operator B The submitted data version number is 2 The current version of the database record is also 2 , not satisfied   "The submitted version must be greater than the current version of the record in order to perform an update   "The optimistic lock strategy, therefore, the operator B The submission was rejected. 
update account 
set balance=balance-50, version=version+1 
where id="1" and version=1 
select id, balance, version from account where id="1"; 
 Query results: id=1, balance=1100, version=2

Hibernate, JPA and other ORM frameworks or implementations are the values returned after using the version number and judging UPDATE again


Related articles: