关于php解决并发的一些疑惑
首先 并发我是这样理解的:
2 个人同时下单, 库存只有 1, 那么肯定有一个人无法抢到。也就是说, 库存只会减 1, 订单也只会生成一条。
后来我用 Jmemter 模拟 1000 人同时操作, 发现订单确实只有一个, 而且库存也没有负数, 但是我并没有做什么锁啊或者队列这些一谈到并发就会涉及到的东西。
$a
是查询到的库存
$b = $a-1;
if($b>=0){
生成订单
修改库存
}
如果没有 if
判断, 确实会负。但是如果加了这个 if
判断就库存只减少 1, 订单只有一条
那么我的问题来了, 加个 if
判断就能解决并发? 还是说实际上真正要处理的是模拟测试后出现的错误率 (Jmemter, 模拟 1000 人, error:59.5%), 或者其他?
请各位大佬解答, 如果我的思路有错误, 也请毫不留情
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
你说的订单问题,其实是:高并发场景下,如何正确扣减库存的问题
if($b>=0)
这样的判断,在高并发的场景下并不使用,因为这样的业务逻辑判断并不是原子操作
,所以存在脏读
的可能。例如:
如何解决这个问题呢?
加锁;
队列:改并行为串行,依次扣减;
操作转换为原子操作;
不光是数据库操作,高并发场景下,还可能会面对什么问题呢:
单点问题(当然 非刚并发场景也会面临这个问题,但是高并发场景,此问题尤为突出)
最大连接数问题,eg.
web 服务器
数据库
...数据安全问题,eg.
脏读
重复操作
最好使用Redis做原子锁 因为它是线程安全的
1、将字段设置为UNSIGNED,然后基础sql如 set a=a-1 where a>0,不要先读出来,然后再操作,可以在一定程度上减少你的并发为负数的可能
2、串行化操作,比如使用redis队列
3、使用redis或者别的原子递增模拟锁操作,比如 incr("lock") == 1 { 进行减操作; 删除lock)} else 自旋,直到得到锁进行以上操作,或者超时
基本上php解决如上问题的思路!!
如果插入的订单没有数据库唯一性冲突,应该会生成多个订单。建议在if里面先sleep(rand(0,5))再执行插入订单操作,这样模拟更真实些。
实际中可能是这样的,你的$a是查询出来的对吧,有可能在高并发的场景下你走了if判断但是没有改变库存的情况下,其他请求已经在读数据了,这个时候你读到的$a就是没减少库存时候的状态。这只是其中一点,最可怕的是如果你用的mysql数据库,查询和修改都会加锁在并发很高的情况下可能会把表直接锁死,其他的操作在等待中,一旦等待执行时间过长、执行过多,数据库就挂掉了。还有你这个测试1000并发的时候使用的代码并不是实际业务中的代码,因为如果减库存这种操作,肯定会有很多相关的逻辑判断,1000个并发执行起来就没你想象的这么完美了。