Today, here, we will discuss whether you choose optimistic locking or pessimistic locking for concurrency control in the actual production process. How to choose the application environment of these two locks?

In a real production environment, pessimistic locking is very convenient and simple to use if the concurrency is small. However, pessimistic locking can cause significant performance problems if the concurrency of the system is very high, so optimistic locking is preferred

Pessimistic locking assumes that there is a high probability that other users will attempt to access or change the object you are accessing or changing, so in a pessimistic locking environment, the object is locked before you start changing it, and the lock is not released until you commit your changes. The disadvantage of pessimistic locking is that whether it is a page lock or a row lock, the lock may be held for a long time, which may limit the access of other users for a long time. In other words, pessimistic locking has poor concurrent access.

Optimistic locks assume that there is little chance that other users will attempt to change the object you are changing, so optimistic locks hold the object until you are ready to commit your changes, and leave the object unlocked when you read and change it. It can be seen that the locking time of optimistic lock is shorter than that of pessimistic lock, and optimistic lock can obtain better concurrent access performance with larger lock granularity.

But if the second user reads the object just before the first user commits his changes, then when he commits his changes, the database will discover that the object has changed, and the second user will have to re-read the object and make changes. This means that in an optimistic locking environment, the number of concurrent users reading objects increases. Using version control systems as an example, let’s talk about the two most basic concurrency problems.

Missing updates:

Zhang wanted to modify the method a in the source code, while she was modifying, Li opened the file, modified the method B and saved the file, etc. After Zhang finished modifying, save the file, Li’s modification was overwritten.

Inconsistent reading:

Xiao Zhang wants to know how many classes there are in the package. The package is divided into two sub-packages: A and B. Zhang opens package A and sees seven classes. Suddenly, Xiao Zhang received a phone call from his wife. While Xiao Zhang was answering the phone, Xiao Li added 2 categories to package A and 3 categories to package B (the original package B had 5 categories).

After answering the phone, Xiao Zhang opened package B and saw 8 classes. It was natural to come to the conclusion that there were 15 classes in the package.

Unfortunately, 15 is never the right answer. The correct answer was 12 (7+5) before Xiao Li changed it to 17 (9+8). Both answers are correct, although one is not current. But 15 is wrong, because zhang’s readings are inconsistent.

Summary: Inconsistent reading means that you read two kinds of data, both of which are correct, but not both of which are correct at the same time.

Isolation and immutability:

In enterprise applications, isolation and immutability are commonly used to resolve concurrent conflicts.

Concurrency problems arise only when multiple activities (processes or threads) simultaneously access the same data. A natural idea is to allow only one activity to access data at a time. This problem can be solved if Chang opens the file and no one else is allowed to open it, or if other people can only open the copy in read-only mode.

Isolation is a great way to reduce the possibility of errors. It’s common to see programmers bogged down in concurrency issues, thinking about concurrency after every piece of code is written, and it’s exhausting. We can use isolation techniques to create isolated areas, and when programs enter isolated areas, they don’t have to worry about concurrency. Good concurrency design is about creating isolated areas like this and keeping the code running in them as much as possible.

** Another way to think about it: ** Concurrency problems can only occur if you need to modify shared data, so we can avoid concurrency problems by making shared data “immutable”. Of course, we can’t make all data immutable, but if some data are immutable, we can relax our nerves when working on them concurrently.

Optimistic concurrency control, pessimistic concurrency control:

What if the data is mutable and cannot be isolated? The two most commonly used controls in this situation are optimistic concurrency control and pessimistic concurrency control.

Suppose Xiao Zhang and Xiao Li want to modify the same file at the same time. With the optimistic lock, both of them can open the file and make changes. If Zhang submits the content first, there is no problem, and his changes are saved to the server. But li gets into trouble when he commits. The version control server detects a conflict between the two changes, li’s reference is specified, and Li decides how to handle the situation (for most version control software, zhang’s changes are read and marked, and Li decides whether to merge or not).

If a pessimistic lock is used and Zhang checks out the file first, Li cannot check out the same file again until Zhang submits his changes.

I suggest you think of optimistic locks as a means of conflict detection, and pessimistic locks as a means of conflict avoidance. (Technically, optimistic locks aren’t really “locks,” but the name has gotten around, so keep using them.) Some older version control systems, such as VSS 6.0, use pessimistic locking. Modern version control systems generally support both and use optimistic locking by default.

Both locks have advantages and disadvantages. Optimistic locking can improve concurrent access efficiency, but if a conflict occurs, it can only be thrown up and then restarted. Pessimistic locks can avoid conflicts, but reduce efficiency.

The choice of which lock to use depends on the frequency of access and the severity of conflicts once they occur. Optimistic locks can be used if the probability of the system being accessed concurrently is low, or if the consequences of the conflict are not serious (the so-called consequences should mean that the transaction that detected the conflict fails and must be restarted), otherwise pessimistic locks can be used.

We can use two forms of concurrency control strategy: optimistic concurrency control and pessimistic concurrency control.

Suppose Martin and David both want to edit the Customer file at the same time. With an optimistic locking strategy, both of them get a Copy of the file and can edit it freely. Assuming David completes the work first, he can update his changes without difficulty. However, when Martin wants to commit his changes, the concurrency control strategy kicks in.

Source control detects a conflict between Martin’s changes and David’s changes, rejects Martin’s submission, and it is Martin’s responsibility to figure out how to handle the situation. If you use a pessimistic locking strategy, as long as someone takes out the file first, no one else can edit the file. Therefore, if Martin fetched the file first, David could only work on the file after Martin completed the task and submitted it.

If optimistic locking is about conflict detection, pessimistic locking is about conflict avoidance. Both strategies can be used in real-world source control systems, but most source developers today prefer to use optimistic locking strategies.

There is a valid argument that optimistic locks aren’t really locks, but the term is too convenient and widespread to ignore.

Both strategies have advantages and disadvantages. The problem with pessimistic locking is that it reduces concurrency. While Martin was editing a file he had locked, everyone else had to wait. Anyone who has used pessimistic source control knows how frustrating this can be. With enterprise data, the situation is often made worse by the fact that as long as someone is editing, no one else can read it, let alone edit it.

An optimistic locking strategy allows people a little more freedom, since it is only at commit time that there is likely to be a block. The problem with this strategy is what happens when there’s a conflict? In fact, everyone after David must read the version that David changed when submitting, figure out how to merge their changes with David’s, and then submit a new version that has been revised.

With source control, there is no trouble in doing this. In many cases, the source control system does automate merging, and even when it doesn’t, it makes it easy for users to see the difference between different file versions. However, business data is often difficult to merge automatically, so you often have to throw away the original and start from scratch.

The criteria for choosing between optimistic and pessimistic locks are the frequency and severity of conflicts. If conflicts are rare, or the consequences of conflicts are not serious, optimistic locking is generally preferred because it provides better concurrency and is easier to implement. However, if the outcome of the conflict is painful to the user, then a pessimistic strategy is needed.

The limitations of optimistic locking are:

It is only discovered when the data is committed that a business transaction is about to fail, and in some cases the cost of discovering failure too late can be high. It can take an hour for a user to enter the details of a lease, and too many errors can make the user lose confidence in the system. Another approach is to use pessimistic locks, which detect errors early but are difficult to program and reduce the flexibility of the system.

Note: The above is a text description of the concepts and solutions of optimistic locking strategy and pessimistic locking strategy in concurrency control. Now I will describe how to implement optimistic locking strategy and pessimistic locking strategy in the project.

Implementation method of optimistic lock strategy:

It is to use transactions in C# or SQL to implement data operation and roll back if it is not successful. Personally, I feel that the ticket selling system of railway station also operates in this way. We see a small amount of tickets on the display screen, but we can not print them out when we go to buy them.

Pessimistic lock strategy implementation method:

1. On a normal ASPX page, when the user clicks submit, change the enabel of submit and related buttons to False until the submit event is complete, and then change back. In addition, in the data layer, each time a data change is committed, it is necessary to determine whether the previous state of the data has changed to prevent concurrent changes.

In jquery, you can set a global variable. When submitting, the state of the global variable will be judged first. If submission is not allowed, the global variable will be returned directly. Then change the global variable to allow commit.

3, the popup window to modify the page, the modal popup, such as the Web page, can use window.show ModalDialog() to realize the modal open modification page, to ensure that only one modification page is always opened. This is pessimistic locking of data from the data manipulation page, not pessimistic locking in the database.

I hope the above content can help you. Many PHPer will encounter some problems and bottlenecks when they are advanced, and they have no sense of direction when writing too many business codes. I have sorted out some information, including but not limited to: Distributed architecture, high scalability, high performance, high concurrency, server performance tuning, TP6, Laravel, Redis, Swoft, Kafka, Mysql optimization, shell scripting, Docker, microservices, Nginx, etc. Many knowledge points can be free to share with you