减库存数量为 num
update mytable set inventory = inventory-num WHERE id= 1 and inventory >=num
这样减库存不用加乐观锁和悲观锁,也可以是吧
但是有什么弊端吗
1
sadfQED2 2021-11-20 12:19:33 +08:00 via Android
可能减成负的呗
|
2
vvhhaaattt 2021-11-20 12:23:18 +08:00 via Android
除非事务隔离是串行化,否则怎么看都会有问题吧
查询跟修改操作分两步走,并发时应该会库存少减,超卖吧 |
3
vvhhaaattt 2021-11-20 12:27:00 +08:00 via Android
@vvhhaaattt 不对,单条语句似乎会加行锁,可能这么写也没问题?
|
4
zjsxwc 2021-11-20 12:29:20 +08:00
有效解决并发数据一致性问题,我能想到就两种方式:
一种是用增量式记录的方式,每次数据获取(比如这里的库存)其实是根据历史记录“算”出来的,这里可以通过缓存中间计算、定时归档等方式加速“算”的速度,缺点是占用空间比较大。 一种就是单一队列方式,就是排队序列化处理,缺点是时间长速度慢。 |
5
dqzcwxb 2021-11-20 12:30:21 +08:00
跨库失效,多表失效
要解决并发只能是 redis 或者队列串行处理 |
6
bigbyto 2021-11-20 12:39:28 +08:00 via iPhone
为啥都在顾左右而言他?我认为这条是没问题的,你那条 sql 会给 id 为 1 的索引加 X Lock ,其他事务想修改必须等你释放锁,因此在默认的隔离级别下是可以保证一致性的。
除了 READ_UNCOMMITED 不行,其他隔离级别应该都没问题。 |
7
zjsxwc 2021-11-20 12:50:48 +08:00
|
9
zjsxwc 2021-11-20 13:04:08 +08:00
@sagaxu 哦哦 ,可以 通过 查看执行后 affected rows count 判断是否失败
``` update mytable set inventory = inventory-num WHERE id= 1 and inventory >=num ``` |
10
huang119412 2021-11-20 13:15:58 +08:00
update 本来就可以当成分布式锁。直接更新不就是悲观锁?缺点是并发低,竞争大。而且你要理解 rows affected ,rows matched 的区别。
|
11
mazyi 2021-11-20 13:39:32 +08:00 via iPhone
update 就是锁呀,实现业务没问题,就是可能效率有问题,所以才想那么多 cache 提前处理
|
12
IvanLi127 2021-11-20 14:12:51 +08:00 via Android
我感觉没啥问题
|
13
markgor 2021-11-20 14:14:36 +08:00
該說的上面都說了,
num 只要前面有做判斷一般都沒問題,(如傳入負數)。 |
14
Jooooooooo 2021-11-20 14:14:59 +08:00
这条语句没问题
就是效率会有问题, 一万个人同时来减就卡死了 一般都会放在缓存里面过一道 |
15
documentzhangx66 2021-11-20 15:28:00 +08:00
真正的生产系统,业务流程代码,需要考虑:
1.健壮性 比如数据库、子业务系统突然崩溃;比如机房停电。 2.可调试性 某个环节出了 bug ,系统设计上需要做到在最短时间内,通过某种方式进行 bug 追踪。 3.记录日志 比如细化的日志级别,连 if else 或 switch case 走的是哪条分支,都需要记录。 以上这些功能,在流程设计与实现时,还需要考虑事务成功与失败时的处理方案,需要考虑并行时如何按顺序操作资源来确保不发生死锁,需要考虑如果某个环节出现了性能瓶颈应该如何处理,等等。 这就是为什么真正健壮的系统,一个简单的业务操作,数据库的业务 SQL 都可以有上千行的原因。 |
16
leafre 2021-11-20 15:40:25 +08:00
这句话就是乐观锁
缺点就是高并发大部分都是失败,一般不采用 什么你说不是高并发?那为什么用锁 |
17
fgwmlhdkkkw 2021-11-20 16:26:22 +08:00
@documentzhangx66 😅😅😅😅😅😅😅😅😅😅😅
|
18
kikione OP @Jooooooooo 没有那么高的并发,同时有 10 个人就不错了
|
20
jsdi 2021-11-20 16:44:10 +08:00
没啥问题 但是 update 是当前读,会加悲观锁,也就是写锁,效率上会有问题
|
23
as9567585 2021-11-20 18:57:00 +08:00
乐观锁
|
24
encro 2021-11-20 19:10:45 +08:00
应该没有问题,
如果不相信可以用`select balance from user for update`这样的, 性能也没有任何问题。 |
25
bucketcheng 2021-11-20 20:56:41 +08:00 1
read commit 模式以上都没有问题,主要是看你事务大小,你如果事务时间比较耗时,而且并发比较高的话,可能话等锁超时,就看并发量
|
26
bucketcheng 2021-11-20 20:57:13 +08:00
都是操作这条库存行的前提
|
27
gosidealone 2021-11-20 21:46:52 +08:00
@bucketcheng 对的,我公司和楼主也是一模一样的处理方法,然后事务耗时,并发高,就出问题了。有什么好的解决方法吗
|
28
bucketcheng 2021-11-20 22:05:12 +08:00
@gosidealone 比较简单方式是 把更新这个冲突最频繁的放在最后更新,减少行锁时间,其次是减少事务粒度,及合并更新,在就架构上做改造,改动就比较大了,我们在这方面做了挺多工作的,
|
29
sujin190 2021-11-20 22:10:52 +08:00 1
sql 没问题,但是实际业务中意义及用处不大,正常的订单中不可能只买一件商品,以及下单过程中还有优惠折扣促销处理等等的流程,这样即意味着大量的回滚流程且开事务时间过长,效率不会很高,而且多件下单的时候很容易导致死锁问题
|
30
gosidealone 2021-11-20 22:21:54 +08:00
@bucketcheng 那我想问下,放在最后更新为什么能减少行锁时间?因为事务没提交完就一直锁着吗
|
31
sujin190 2021-11-20 22:36:18 +08:00
@gosidealone #30 必然的啊,否则事务用来干嘛的,所以实际业务中多件商品下单这种操作如果不排序,分分钟死锁
|
32
xuanbg 2021-11-21 07:30:10 +08:00
sql 没问题,扯东扯西各种问题的,大概都是在 show 自己懂得多吧,好为人师果然是一种人性。好吧,我从业务角度来说我对减库存的理解。
其实,除了特殊商品,一般来说就是数量有限,譬如车票、二手货等不能超卖,正常的商品谁管你超卖多少啊。生产 /采购部门自然会把你卖掉的货生产出来 /采购回来补上库存缺口。 |
33
myd 2021-11-21 10:03:48 +08:00
没问题,不会超卖。
|
34
xe2vforesu 2021-11-21 11:48:59 +08:00
没问题,不会多卖,也不会少卖。但是高并发场景下存在锁竞争,死锁检测等性能问题,进而导致系统处理能力降低。
|
35
Chad0000 2021-11-22 03:35:59 +08:00 via iPhone
我的方案是直接使用 queue ,一个消费者负责增减库存。库存在 Redis 中,带版本号。读从 Redis 中,写同时写。版本号在写时加入判断可确保不会有脏数据。
queue 性能还可以,一般你的系统不会一秒上万修改库存请求。 以上方案已在生产环境使用 |
36
love2020 2021-11-22 08:51:59 +08:00
你需要考虑并发修改为负数、ABA 问题(版本),幂等性。 通常可以采用 SELECT & SET & CAS 解决。
|
37
jorneyr 2021-11-22 09:04:52 +08:00
没问题,MySQL 有行锁
|
38
sujin190 2021-11-22 09:40:12 +08:00
@Chad0000 #35 那你这如何和下单流程的事务做绑定呢,先减库存的话,万一你这下单最后又失败了,你这库存如何恢复,后减库存的话,中间有时间差,也不能保证不超卖吧
|
40
sujin190 2021-11-22 10:06:59 +08:00
@Chad0000 #39 不,我说的只是下单失败的情况,按这个逻辑,Lock 库存需要在商品有效性检查的库存校验时就要完成,而实际业务中下单后续商品还有其它校验流程,接着还有地址检查,优惠券营销促销折扣,风控校验等等一系列过程,这些都是要做一致性绑定的,更不要说后面的支付流程了
|
41
Chad0000 2021-11-22 10:24:59 +08:00
@sujin190 #40
下单最后又失败了:订单系统做最终一致性检查,对于失败的已经出库的,及时通知库存回库,简单讲你下单后,生成一个若干分钟后的任务检查是否完成支付和出库。下单前已经锁定库存,所以有时间差也不会导致超卖除非锁库超时自动退回。 超卖问题:这个无法完全避免,你总会在各个流程遇到意外情况:比如拖着不付款或付款时间太长的,导致锁库超时已返回库存。这时下单使用的就是未锁定的库存 - 不能保证有货。付款锁定和库存锁定这两个东西即使一致也无法保证出库,因为付款可以因为第三方支付而拖时间。所以都只能满足大部分场景,然后通过最终一致化完成少数不正常的。 |
42
shanghai1943 2021-11-22 10:33:18 +08:00
不考虑高并发的话,这种实现方式简单,只要判断 affected rows 是否等于 1 就行了。
|
43
haython 2021-11-22 11:16:22 +08:00
淘宝以前就是这么干的
|
44
hangszhang 2021-11-22 12:37:26 +08:00
没问题,就是性能不高
|
45
dgr007 2021-11-22 12:45:52 +08:00
没问题,性能很好的
可能需要解决的是单条记录热点更新问题 |
46
bsg1992 2021-11-23 10:13:01 +08:00
SQL 没问题
你这个就是乐观锁,弊端就是并发量大的话会导致更新失败。 特别是针对某条热点数据会一直失败,这个需要解决一下。 一般这种情况你看的场景 1.是否允许超卖,不允许超卖的话另开一个监控程序发现超卖就直接退款 /或者不发货 当然这个是业务层面上的解决方案。 2.用队列进行消费,看你消费端的速度了。 3.就是实时计算库存数量,根据消耗库存记录算出来剩余库存 |
47
hui314 2021-11-23 10:55:05 +08:00
不想改动的话,有个最简单方法,新增一个字段 version,减库存 sql:update mytable set inventory = inventory-num , version = version+1 WHERE id= 1 and inventory >=num and version = {version};
即使并发也只有一个能成功,但是这样的话,只能一个人成功下单。 |