MySQL handles high concurrency to prevent inventory oversold

Today, Mr. Wang taught us another lesson. In fact, mysql handles high concurrency and prevents oversold inventory. Last year, Mr. Wang already mentioned it; but unfortunately, even if everyone understood it at the time, in reality In development, there is still no awareness of this aspect. Today, I will sort out this problem with some of my understanding, and hope that there will be more courses like this in the future.

Let’s first describe the problem of oversold inventory: General e-commerce websites will encounter activities such as group buying, seckill, and special offers, and such activities have a common feature that the traffic surges, thousands or even tens of thousands of people snap up a commodity. However, as an active commodity, the inventory is definitely very limited. How to control the inventory to prevent overbought to prevent unnecessary losses is a headache for many e-commerce website programmers, which is also the most basic problem.

From a technical perspective, many people will definitely think of transactions, but transactions are a necessary condition for controlling inventory oversold, but not a sufficient and necessary condition.

Example:

Total inventory: 4 items

Requester: a, 1 item b, 2 items c, 3 items

The procedure is as follows:

beginTranse (open transaction)

try{

    $result = $dbca->query('select amount from s_store where postID = 12345');

    if(result->amount > 0){

        //quantity is the quantity of inventory requested to be reduced

        $dbca->query('update s_store set amount = amount - quantity where postID = 12345');

    }

}catch($e Exception){

    rollBack (rollback)

}

commit (commit transaction)

The above code is the code we usually write to control the inventory. Most people will write it like this. It seems that the problem is not big, but it actually hides a huge loophole. Database access is actually the access to disk files. The tables in the database are actually files saved on the disk, and even a file contains multiple tables. For example, due to high concurrency, there are currently three users a, b, and c who have entered this transaction. At this time, a shared lock will be generated. Therefore, when selecting, the inventory quantities found by these three users are all 4. , and also note that the results found by mysql innodb are version-controlled, and before other users update without commit (that is, before a new version is generated), the results found by the current user are still the same version;

Then there is the update. If the three users arrive at the update at the same time, the update update statement will serialize the concurrency at this time, that is, the three users who arrive here at the same time are sorted, executed one by one, and an exclusive lock is generated. Before the current update statement is committed, other users wait for execution. After the commit, a new version is generated; after this is executed, the inventory must be negative. However, according to the above description, if we modify the code, there will be no overbought phenomenon. The code is as follows:

beginTranse (open transaction)

try{

    //quantity is the quantity of inventory to be reduced by request
    $dbca->query('update s_store set amount = amount - quantity where postID = 12345');

    $result = $dbca->query('select amount from s_store where postID = 12345');

    if(result->amount < 0){

       throw new Exception('Insufficient stock');

    }

}catch($e Exception){

    rollBack (rollback)

}

commit (commit transaction)


Alternatively, a more concise way:

beginTranse (open transaction)

try{

    //quantity为请求减掉的库存数量
    $dbca->query('update s_store set amount = amount - quantity where amount>=quantity and postID = 12345');

}catch($e Exception){

    rollBack(回滚)

}

commit(提交事务)


=====================================================================================

1、在秒杀的情况下,肯定不能如此高频率的去读写数据库,会严重造成性能问题的
必须使用缓存,将需要秒杀的商品放入缓存中,并使用锁来处理其并发情况。当接到用户秒杀提交订单的情况下,先将商品数量递减(加锁/解锁)后再进行其他方面的处理,处理失败在将数据递增1(加锁/解锁),否则表示交易成功。
当商品数量递减到0时,表示商品秒杀完毕,拒绝其他用户的请求。


2、这个肯定不能直接操作数据库的,会挂的。直接读库写库对数据库压力太大,要用缓存。
把你要卖出的商品比如10个商品放到缓存中;然后在memcache里设置一个计数器来记录请求数,这个请求书你可以以你要秒杀卖出的商品数为基数,比如你想卖出10个商品,只允许100个请求进来。那当计数器达到100的时候,后面进来的就显示秒杀结束,这样可以减轻你的服务器的压力。然后根据这100个请求,先付款的先得后付款的提示商品以秒杀完。


3、首先,多用户并发修改同一条记录时,肯定是后提交的用户将覆盖掉前者提交的结果了。

这个直接可以使用加锁机制去解决,乐观锁或者悲观锁。
乐观锁,就是在数据库设计一个版本号的字段,每次修改都使其+1,这样在提交时比对提交前的版本号就知道是不是并发提交了,但是有个缺点就是只能是应用中控制,如果有跨应用修改同一条数据乐观锁就没办法了,这个时候可以考虑悲观锁。
悲观锁,就是直接在数据库层面将数据锁死,类似于oralce中使用select xxxxx from xxxx where xx=xx for update,这样其他线程将无法提交数据。
除了加锁的方式也可以使用接收锁定的方式,思路是在数据库中设计一个状态标识位,用户在对数据进行修改前,将状态标识位标识为正在编辑的状态,这样其他用户要编辑此条记录时系统将发现有其他用户正在编辑,则拒绝其编辑的请求,类似于你在操作系统中某文件正在执行,然后你要修改该文件时,系统会提醒你该文件不可编辑或删除。


4、不建议在数据库层面加锁,建议通过服务端的内存锁(锁主键)。当某个用户要修改某个id的数据时,把要修改的id存入memcache,若其他用户触发修改此id的数据时,读到memcache有这个id的值时,就阻止那个用户修改。


5、实际应用中,并不是让mysql去直面大并发读写,会借助“外力”,比如缓存、利用主从库实现读写分离、分表、使用队列写入等方法来降低并发读写。

Guess you like

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