This article mainly shares with you how to solve high concurrency in PHP. When it comes to rush buying, flash sales, lottery draws, ticket grabbing and other activities, in order to avoid overselling, the inventory quantity is limited, but if the number of people placing orders at the same time exceeds the inventory quantity, This will lead to oversold problems. So how do we solve this problem? My idea is as follows (pseudocode):
sql1: Query product inventory
if(库存数量 > 0) { //生成订单... sql2:同时库存-1 }
When there is no concurrency, the above process looks like It's perfectly normal. Suppose two people place an order at the same time, and there is only 1 item in stock. In the sql1 stage, the inventory queried by both people is >0, so they eventually execute sql2, and the inventory finally becomes -1, exceeding Sold, 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, so there will be no concurrency. There is a problem, but it requires additional background processes and delay issues, 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 +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 Of course, when an order is generated, the inventory is -1, but don't worry, check the inventory again and the return result is 3) to 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 that the inventory is insufficient. . 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.
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, so other concurrently executed selects If for update attempts to select the same row, exclusion will occur (need to wait for the row lock to be released), thus achieving the lock effect. 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.
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, "optimistic" believes that the lock acquisition 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. 根据获取的数据进行业务操作,得到new_data和new_version3. UPDATE SET data = new_data, version = new_version WHERE version = old_versionif (updated row > 0) { // 乐观锁获取成功,操作完成 } else { // 乐观锁获取失败,回滚并重试 }
乐观锁是否在事务中其实都是无所谓的,其底层机制是这样:在数据库内部update同一行的时候是不允许并发的,即数据库每次执行一条update语句时会获取被update行的写锁,直到这一行被成功更新后才释放。因此在业务操作进行前获取需要锁的数据的当前版本号,然后实际更新数据时再次对比版本号确认与之前获取的相同,并更新版本号,即可确认这之间没有发生并发的修改。如果更新失败即可认为老版本的数据已经被并发修改掉而不存在了,此时认为获取锁失败,需要回滚整个业务操作并可根据需要重试整个过程。好吧,在此唠叨总结下这两个锁:
乐观锁在不发生取锁失败的情况下开销比悲观锁小,但是一旦发生失败回滚开销则比较大,因此适合用在取锁失败概率比较小的场景,可以提升系统并发性能
乐观锁还适用于一些比较特殊的场景,例如在业务操作过程中无法和数据库保持连接等悲观锁无法适用的地方
3.根据update结果来判断,我们可以在sql2的时候加一个判断条件update table set 库存=xxx where 库存>0,如果返回false,则说明库存不足,并回滚事务。
4.借助文件排他锁,在处理下单请求的时候,用flock锁定一个文件,如果锁定失败说明有其他订单正在处理,此时要么等待要么直接提示用户"服务器繁忙"
大致代码如下:
阻塞(等待)模式
<?php$fp = fopen("lock.txt", "w+");if(flock($fp,LOCK_EX)) //锁定当前指针,,,{ //..处理订单 flock($fp,LOCK_UN); }fclose($fp);?>
非阻塞模式
<?php$fp = fopen("lock.txt", "w+");if(flock($fp,LOCK_EX | LOCK_NB)) { //..处理订单 flock($fp,LOCK_UN); }else{ echo "系统繁忙,请稍后再试"; } fclose($fp);?>
5.如果是分布式集群服务器,就需要一个或多个队列服务器 小米和淘宝的抢购还是有稍许不同的,小米重在抢的那瞬间,抢到了名额,就是你的,你就可以下单结算。而淘宝则重在付款的时候的过滤,做了多层过滤,比如要卖10件商品,他会让大于10的用户抢到,在付款的时候再进行并发过滤,一层层的减少一瞬间的并发量。
6.使用redis锁 product_lock_key 为票锁key 当product_key存在于redis中时,所有用户都可以进入下单流程。 当进入支付流程时,首先往redis存放sadd(product_lock_key, “1″),如果返回成功,进入支付流程。如果不成,则说明已经有人进入支付流程,则线程等待N秒,递归执行sadd操作。
当然类似于淘宝双11的疯抢架构远远比我说滴这些复杂多啦....更多解决方案需要不停滴去实战中获取心得....大家有好的解决思路清随时共享留言哈。
相关推荐:
The above is the detailed content of How to solve high concurrency in php. For more information, please follow other related articles on the PHP Chinese website!