Home  >  Article  >  Some thoughts on high concurrency solutions in PHP

Some thoughts on high concurrency solutions in PHP

无忌哥哥
无忌哥哥Original
2018-06-27 14:37:592189browse

When it comes to rush buying, flash sales, lottery draws, ticket grabbing and other activities, in order to avoid oversold, the inventory quantity is limited. However, if the number of people placing orders at the same time exceeds the inventory quantity, it will lead to oversold problems. So how do we solve this problem? My idea is as follows (pseudocode): sql1: Query product inventory if (inventory quantity > 0) { //Generate order...
sql2: Simultaneous inventory-1}

When there is no concurrency, the above process seems normal. Assume that two people place orders at the same time, and there is only one inventory. In the sql1 stage, the inventory queried by both people is > ;0, so sql2 was eventually executed, and the inventory finally became -1, which is oversold. This is not the result we want.

I have summarized the more popular ideas to solve this problem:

1. Use an additional single process to process a queue, put the order requests in the queue, and process them one by one. There are concurrency issues, but additional background processes and delay issues need to be opened, which will not be considered here for now. Here I can use message queue, we often use Memcacheq and Radis. For example: if there are 100 tickets for users to grab, then they can put these 100 tickets in the cache and do not lock them when reading and writing. When the amount of concurrency is large, about 500 people may successfully grab tickets, so that requests after 500 can be directly transferred to the static page at the end of the event. It is impossible for 400 of the 500 people who enter to get the product. Therefore, only the first 100 people can purchase successfully according to the order in which they enter the queue. The next 400 people will go directly to the event end page. Of course, entering 500 people is just an example. You can adjust the number yourself. The activity end page must use a static page, not a database. This reduces the pressure on the database.

2. MySQL optimistic locking means that for example, the total inventory is 2, and when the rush buying event is submitted, the inventory is immediately set to 1, then the inventory is 3 at this time, and then after the order is generated, the inventory is queried again before updating the inventory. (Because the order is generated, of course the inventory is -1, but don't worry, check the inventory again and the return result is 3). See if it is consistent with the expected inventory quantity (the expected inventory here is 3). If it is inconsistent, roll back and prompt the user. Inventory shortage. Here we talk about pessimistic locks. Some friends may ask, then there must be optimistic locks, right?? Here I will briefly talk about the pessimistic and optimistic locks that I know

Pessimistic locks and optimistic locks are two common ones The resource concurrency lock design idea is also a very basic concept in concurrent programming. This article will provide a comparative and systematic introduction to the implementation of these two common lock mechanisms on database data.

Pessimistic Lock

The characteristic of pessimistic lock is to acquire the lock first and then perform business operations. That is, "pessimistic" believes that acquiring the lock is very likely to fail, so it is necessary to first Make sure to obtain the lock successfully before performing business operations. The commonly referred to as "one lock, two checks and three updates" refers to the use of pessimistic locks. Generally speaking, pessimistic locking on the database requires support from the database itself, that is, pessimistic locking is implemented through the commonly used select...for update operation. When the database executes select for update, it will acquire the row lock of the data row in the selection. Therefore, if other concurrently executed select for update attempts to select the same row, exclusion will occur (need to wait for the row lock to be released), so the lock effect is achieved. . The row lock acquired by select for update will be automatically released at the end of the current transaction, so it must be used within the transaction.

One thing to note here is that different databases have different implementation and support for select for update. For example, Oracle supports select for update no wait, which means that if the lock cannot be obtained, an error will be reported immediately, instead of Wait, mysql does not have the no wait option. Another problem with MySQL is that all scanned rows will be locked during the execution of the select for update statement, which can easily cause problems. Therefore, if you use pessimistic locking in mysql, be sure to use the index instead of a full table scan.

Optimistic Lock(Optimistic Lock)

The characteristics of optimistic lock are to perform business operations first, and do not take the lock until it is absolutely necessary. That is to say, we are "optimistic" and believe that the lock will most likely be successful, so just take the lock after the last step of actually updating the data after completing the business operation.

The implementation of optimistic locking on the database is completely logical and does not require special support from the database. The general approach is to add a version number or timestamp to the data that needs to be locked, and then implement it as follows:

1. SELECT data AS old_data, version AS old_version FROM…;2. According to the obtained data Perform business operations and obtain new_data and new_version3. UPDATE SET data = new_data, version = new_version WHERE version = old_versionif (updated row > 0) { // Optimistic lock acquisition is successful, operation completed} else { // Optimistic lock acquisition fails, return Roll and try again}

It actually doesn’t matter whether optimistic locking is in a transaction. The underlying mechanism is as follows: Concurrency is not allowed when updating the same row inside the database, that is, the database will obtain the updated row every time it executes an update statement. The write lock is not released until the row is successfully updated. Therefore, before the business operation is performed, obtain the current version number of the data that needs to be locked, and then compare the version number again to confirm that it is the same as the one obtained before when the data is actually updated, and update the version number to confirm that no concurrent modifications have occurred. If the update fails, it can be considered that the old version of the data has been concurrently modified and no longer exists. At this time, it is considered that the lock acquisition failed, and the entire business operation needs to be rolled back and the entire process can be retried as needed.

Here is a summary of these two locks:

1. The cost of optimistic locking is smaller than that of pessimistic locking when no lock failure occurs, but once a failure occurs, the cost of rollback will be It is relatively large, so it is suitable for use in scenarios where the probability of lock failure is relatively small, which can improve the concurrency performance of the system.

2. Optimistic locking is also suitable for some special scenarios, such as when the connection to the database cannot be maintained during business operations and other places where pessimistic locking cannot be applied.

3. Judging based on the update results, we can add a judgment condition update table set inventory=xxx where inventory>0 in sql2. If false is returned, it means that the inventory is insufficient and the transaction is rolled back.

4. With the help of file exclusive lock, when processing an order request, use flock to lock a file. If the lock fails, it means that other orders are being processed. At this time, the user must either wait or directly prompt "server busy". The approximate code is as follows:

Blocking (waiting) mode

$fp = fopen("lock.txt", "w+");if(flock($fp,LOCK_EX)) { // 锁定当前指针,,,
    //..处理订单
    flock($fp,LOCK_UN);
}fclose($fp);

Non-blocking mode

$fp = fopen("lock.txt", "w+");if(flock($fp,LOCK_EX | LOCK_NB)) {    //..处理订单
    flock($fp,LOCK_UN);
} else {  echo "系统繁忙,请稍后再试";
}fclose($fp);

5. If it is a distributed cluster server, one or more queue servers are required Xiaomi It is slightly different from Taobao's rush buying. Xiaomi focuses on the moment of rush buying. Once you grab the quota, it is yours, and you can place an order and settle the payment. Taobao, on the other hand, focuses on filtering during payment. It has implemented multiple layers of filtering. For example, if it wants to sell 10 items, it will let more than 10 users grab them. It will then perform concurrent filtering during payment, reducing the number of items layer by layer in an instant. amount of concurrency.

6. Use redis lock product_lock_key as the ticket lock key. When product_key exists in redis, all users can enter the order process. When entering the payment process, first store sadd(product_lock_key, “1″) in redis. If the return is successful, enter the payment process. If it fails, it means that someone has already entered the payment process, and the thread waits for N seconds and performs the sadd operation recursively.


Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn