1
lihongjie0209 2018-11-20 17:37:56 +08:00
你的事务级别是什么?
|
2
iloveyou 2018-11-20 18:09:01 +08:00
悲观锁乐观锁
|
3
WeaponXu 2018-11-20 18:15:42 +08:00
取数据的时候要锁一下
|
4
mmdsun 2018-11-20 18:24:33 +08:00 via Android
update number set x=x-1 where x >0 这样写肯定不会超卖的。RR 隔离级别
|
5
opengps 2018-11-20 18:25:49 +08:00 via Android
秒杀是个高并发的东西,不适合用数据库控制
|
6
gejun123456 2018-11-20 18:28:56 +08:00 via iPhone
你这样写肯定不会的
|
7
blue0125 2018-11-20 18:30:31 +08:00 via Android
据说用 Redis 可以
|
8
xavier007 2018-11-20 18:37:52 +08:00
队列!比如小米原来的秒杀,就是有个中间页,然后通过队列限制进入实际购买页。另外数据库更新也可以利用队列。这样限制了更新数据库的并发量
|
9
zjsxwc 2018-11-20 18:44:40 +08:00 via Android
这种 sql 明显会超卖的啊, mysql 默认的事务级别是允许幻读的, 使用队列处理秒杀是最佳实践.
|
10
ilyh 2018-11-20 19:22:13 +08:00
你这样写是不会的, mysql 默认的隔离级别是可重复读, update set 操作是当前读, 会加锁的. 和幻读没有关系
|
11
luozic 2018-11-20 19:23:21 +08:00 via iPhone
事务,你这只是 update,
|
12
heww 2018-11-20 19:24:05 +08:00 via iPhone
用 select for update 来做
|
13
o00o 2018-11-20 19:29:13 +08:00 via Android
艺高人胆大,我这都是在 Java 端用线程锁判断
|
14
limuyan44 2018-11-20 19:45:05 +08:00 via Android
mysql 默认的 rr 级别不会的,另,这个和上面说的幻读有毛关系。。
|
15
Raymon111111 2018-11-20 19:47:25 +08:00
这样写应该不会超卖啊
|
16
mayday526 2018-11-20 19:50:59 +08:00
默认级别 RR,理论上不会,那么问题来了,为什么这样还是会超卖呢
|
17
leriou 2018-11-20 19:55:24 +08:00
秒杀用 redis 分布式锁服务
|
18
insert000 2018-11-20 19:55:54 +08:00
当并发进来的时候 x-1 可能是同时的,并且 x 设置成非负
|
19
Va1n3R 2018-11-20 19:59:18 +08:00 1
了解一下安全界的条件竞争漏洞~
|
20
cheeseyu1994 2018-11-20 20:00:24 +08:00 via Android
这样会超卖吗?等答案|ω・)
是不是没有对 mysql 操作结果进行判断就直接写入订单了? |
22
qsbaq 2018-11-20 20:02:43 +08:00
高并发的话建议 redis 或者 memcached,频繁更新数据库会增加负载。
https://www.jiloc.com/44364.html |
24
insert000 2018-11-20 20:09:43 +08:00
用 redis SETNX 当锁拦一道,抢到锁的再操作数据库就好了
|
25
tuntunxiong 2018-11-20 20:22:20 +08:00
redis list, rpush rpop
|
26
linbiaye 2018-11-20 22:11:45 +08:00
这个写法,事务级别为 RR, RC, Serialize 都不应该会出现问题才是,是不是没检查返回值?
|
27
oovveeaarr 2018-11-20 22:23:17 +08:00
的确比较奇怪,我也差不多是这种写法不应该会负数的
|
28
alcarl 2018-11-20 22:40:33 +08:00 via Android 1
这跟隔离级别没关系吧又不是 select,update 要先锁记录的,有别的事务锁不了就等着了,因此这条不会出现超卖,但现实中一般不会这么就一个条件就更新,一般 where 里还有别的条件需要先查查 sql 的别的地方
|
29
ppyybb 2018-11-20 22:55:53 +08:00 via iPhone
不讨论秒杀
就这个语句,不可能超卖: 默认是 rr 的,即便是 rc 级别,update 也会加行锁,不存在超卖。 我怀疑你看的教程的正确性 |
30
alex321 2018-11-20 23:03:21 +08:00
redis list len
|
31
37rangers 2018-11-20 23:40:29 +08:00
你给它放到 redis list 里,先到先得每次只能出 list 一个 ,相对会好很多
|
32
jimchen9999 2018-11-21 00:00:55 +08:00 via Android
直接 redis pop 啊 要什么数据库
|
33
cubecube 2018-11-21 00:37:39 +08:00
@ppyybb 我觉得你说得对,帖子里面扯队列,redis 也是够了。就事论事,这个 mysql 语句是基本的 mysql 锁机制。行级别都保证不了一致性的话,mysql 死去算了。楼主其实没说清楚,最后是应用层超卖,还是这个 mysql 控制库存 x 小于 0 了?
|
34
loqixh 2018-11-21 00:57:11 +08:00
没有读 update 影响行数判断是否成功吧?
|
35
myhot21 2018-11-21 08:19:22 +08:00 via Android
并发下,这种 sql 是完全避免不了超卖,加事务也只是降低出现概率,最好的避免方式是改用队列。
|
36
polymerdg 2018-11-21 08:19:50 +08:00
上 redis 很難嘛?
|
37
ljzxloaf 2018-11-21 08:30:35 +08:00
你这是表锁啊,应该不会超卖,但是性能很差
|
38
wmhack 2018-11-21 09:00:17 +08:00 via iPhone
在外层代码里做 x-1 试试
|
39
ccl945 2018-11-21 09:05:52 +08:00 via Android
超卖放 redis,就 mysql 这尿性,没崩就要庆幸了
|
40
irgil 2018-11-21 09:18:42 +08:00
有人能分析一下为何会超卖吗?
|
41
cqu1980 2018-11-21 09:19:13 +08:00
这个只是保证减库存后,库存数不会变成负数,和超卖是两个概念....
|
42
CSDreamer 2018-11-21 09:20:26 +08:00
秒杀请求走队列,消费者去消费,就不出现这种情况,不能完全基于 mysql 做秒杀
|
43
juneszh 2018-11-21 09:20:37 +08:00
难道楼主没有用 ROW_COUNT()来获取 UPDATE 语句的执行结果,而是直接用 EXEC()的返回值?
|
44
cqu1980 2018-11-21 09:23:13 +08:00
估计没做 43 楼的操作~~~~~~
|
45
awanabe 2018-11-21 09:23:52 +08:00 via iPhone
事务搞到串行当然可以避免的 那双十一的订单可能要几年才能处理完吧?
|
46
springGun 2018-11-21 09:27:35 +08:00
为什么不是 where x>0
|
47
ikaros 2018-11-21 09:34:51 +08:00
放在内存里用互斥锁操作,或者先锁表
|
48
kismetX 2018-11-21 09:38:46 +08:00
如果是 innodb,并且加了事务的话,是不会出现负数的情况的,每行数据是都有一个事务版本号的,修改前 innodb 引擎是会检查事务版本号再更改的,这个就是解决前面部分说幻读的机制,而且这也是行级锁,讲道理,如果判断了返回的影响行数,不仅不会成为负数,也不会比库存多订单
|
49
tabris17 2018-11-21 09:42:32 +08:00
我猜 lz 用的 MyISAM 引擎,大家散了吧
|
50
shenhhd 2018-11-21 09:46:59 +08:00
坐等大神终结此问题~~~~
|
51
cyhulk OP @tabris17 不是,这只是我无意间看网上的文章看到的,不信你百度,秒杀超卖,很多都这样写,所以我就很奇怪,1000 个并发去操作也没有问题,所以就问问了,还有大家纠结的锁,其实即使是 RC 隔离级别下,也会有 X 锁,也不会出现问题,这个我一会验证下。
|
52
amon 2018-11-21 10:00:47 +08:00
如果是 InnoDB,加了合适的事务的话,不会有问题吧。
另外秒杀这种还是走队列。 |
53
linxy 2018-11-21 10:02:28 +08:00
按 LZ 的说法,搜了一下来源,原来是别人博客里的。。。。。
|
55
cyssxt 2018-11-21 10:05:28 +08:00 via iPhone
通过数据库锁并不是好的办法 需要在最上层杜绝 比如 200 的的秒杀量 10000 的访问量 后面的人 99800 的请求应该直接返回失败 个人觉得最好的办法是用队列去处理 前端轮回处理秒杀结果
|
57
tingfang 2018-11-21 10:23:43 +08:00
不可能超卖的吧?
|
58
karllynn 2018-11-21 10:25:10 +08:00
这个不会超卖啊,这跟事务也没啥关系,这是数据库的基本保证吧
另外为啥不直接写`where x > 0`… |
59
grandpa 2018-11-21 10:30:13 +08:00
有检查 affected rows 吗
|
62
realpg 2018-11-21 10:48:30 +08:00
事务呢……
|
63
mineqiqi 2018-11-21 11:43:55 +08:00
上 redis 分布式锁是比较好的解决方案
|
64
saltxy 2018-11-21 11:59:04 +08:00
网上的博客都是到处抄,连写错的地方都是一模一样~这条语句顶多就是高并发扛不住,库存是不会减到负数的
|
65
jzmws 2018-11-21 13:12:36 +08:00
数据库把 库存的数量设置为 unsigned 的类型 数据库做个最后的拦截
|
66
Exceptions 2018-11-21 13:13:43 +08:00
这个和事务没关系,update 不管在 innodb 下是行锁还在 myisam 下是表锁, 都不会造成负数的情况。网上博客一大抄,看看就行了,别较真, 真较真起来能气死自己....
|
68
mnhkahn 2018-11-21 14:06:58 +08:00
你们秒杀没量吗?居然数据库减
|
69
micean 2018-11-21 14:10:22 +08:00
贴一下超卖的博客看一下?
|
70
lixikei 2018-11-21 14:40:45 +08:00
秒杀这种场景,直接减数据库,总会有意想不到的惊喜。
|
71
lixikei 2018-11-21 14:42:32 +08:00
秒杀开始前,先将库存信息存到 redis、memcache 里,减库存直接内存减,订单付款结束更新数据库。
|
72
xiaoxiaoan317 2018-11-21 14:50:28 +08:00
高并发,首先要做上游拦截,然后使用内存队列,最后异步处理,mysql 就可以轻轻松松应对了
|
73
qilishasha 2018-11-21 15:04:33 +08:00
设计模式可以看一下……不要相信那些博客上愚蠢的办法(我朝当真是人才济济)…… 12306 坑那么久也是没得说了,可以多看看 12306 的解决方案,不要浪费时间在数据库这种东西的操作上,不要感谢我,我的名字叫雷锋!
|
74
weizhen199 2018-11-21 15:07:31 +08:00
大哥啊,用队列啊,数据库🔒吃不消啊
|
75
cyhulk OP @Exceptions 是行锁没问题,但是不同隔离级别的下的锁类型可能也不一样,X 锁还是 S 锁,这个我也不是特别清楚,其实一直想找个 DBA 问下
|
76
Exceptions 2018-11-21 17:07:57 +08:00
@cyhulk 底层用到的锁肯定不止这两种,排它锁共享锁间隙锁范围锁意向锁等等,事务下每次更新数据,都会用到特定的锁去锁定,然后修改数据存到 redo 日志,旧数据存到回滚段的 undo 日志里,读取都是不加锁的快照读,直接读取 undo 日志的数据,所以 innodb 才适合高并发的互联网应用
|
77
cyhulk OP @Exceptions 不考虑 RC 和 RR 级别,这两个都是严格的 X 锁,RR 还要加间隙锁,我唯一不确定的就是 Read uncommit 级别下的是否是 S 锁,如果是 S 锁,确实可能会存在,但是我实践上没有这个问题
|
78
wentaoliang 2018-11-21 17:53:34 +08:00
为啥上面一堆人在扯事务的隔离级别, 这个事务隔离级别有毛的关系,我觉得这和 update number set x=x-1 where x >= 1 没区别, 会把 x >= 1 的全部加上行锁,所以不会超发
|
79
he583899772 2018-11-21 17:54:56 +08:00
我以前也出现超卖了,后来我用 redis 链表实现的,活动前吧库存缓存到 redis 列表,放在下订单逻辑的前面,卖一个出一个,为空就报错了,不让创建订单,前端也搞搞几层缓解一下并发
|
80
cyhulk OP @he583899772 为啥要用列表呢?直接数字 incrby 不也可以吗?
|
81
he583899772 2018-11-21 18:06:22 +08:00
@cyhulk 都行吧,当初萌新也是摸索过河,只要反正利用 redis 原子性操作就行了,不过订单没付款,自动取消的时候还要反补 redis 活动库存吧
|
82
zerozerone 2018-11-21 18:31:35 +08:00 via Android
老哥找到答案记得帮忙艾特我一下,我也想知道
|
83
cyhulk OP @zerozerone 我自己做过 1000 个并发,不会初心超卖,那个博客说的是错的
|
84
agostop 2018-11-22 13:34:10 +08:00
1、这语句肯定是全表锁,全表遍历
2、既然是全表加锁遍历,当然是不会出现超卖 |