ppgjx 发表于 2022-11-1 17:11

mysql事务请教

我现在遇到一个支付回调问题 我得写法是
1.用户提交订单 订单表生成一条订单记录 并有个state字段标识该订单未支付
2.等到用户支付成功后,微信那边回调到我服务器,会带这个订单id,然后我根据state判断这个订单是否已经支付,如果没有支付就改为支付状态,并发放奖励

现在有个问题来了 并发问题

假如微信服务器一次性回调了两次支付成功请求 而且都是同一个订单id 我们可以理解为两个请求 分别用a,b表示

首先a请求过来了 他查询这个订单id发现订单没有支付 进行支付处理 但是由于某种原因 阻塞了2秒 只读取了数据库这时候b请求过来了 很顺利的把这个订单处理了奖励也发放了 表state也更新为了已支付, 这时候a请求不阻塞了但是他读取的到订单状态还是未支付 它又进行了一次更新操作并又下发了奖励

这就导致发放了两次奖励 这种问题怎么解决呢?

我启动了mysql事务 好像没有用 还是会发放两次奖励






Goldrepo 发表于 2022-11-1 17:22

1.给更新订单状态的方法加锁;
2.更新订单状态时加上订单状态条件,eg:update xxx set status = 1 where id = xxx and status = 0;这样就算同时两个请求过来了,优先执行更新的能正常执行,后更新的会因为状态条件变了而update为0;一旦更新条数为0,则不发放奖励

gaoxiyang1237 发表于 2022-11-1 17:23

这个一般事务控制不了的,应该用分布式锁控制,在操作数据库之前控制加锁。常见有Zookeeper 与redis分布式锁。推荐使用redis分布式锁,对订单号加锁,redis底层是单线程,故不会存在多个线程线程同时操作相同代码(同一个订单)逻辑的问题。具体redis锁实现步骤。百度一下有很多很成熟的方案

stevenliy 发表于 2022-11-1 17:28

Goldrepo 发表于 2022-11-1 17:22
1.给更新订单状态的方法加锁;
2.更新订单状态时加上订单状态条件,eg:update xxx set status = 1 where...

沙发正解

石淞元 发表于 2022-11-1 17:29

同意楼上说法,mysql事务不可能控制这个事务问题

吨吨吨吨 发表于 2022-11-1 17:31

注解式事务加在controller上试试,不行就再试试声明式事务

潇洒三叔 发表于 2022-11-1 17:37

乐观锁 可以解决

还来得及。 发表于 2022-11-1 17:43

给发放奖励加个锁,然后发放奖励事件去判断id and state and 锁

13677661413 发表于 2022-11-1 19:07

数据库自带的事务不好解决,可以使用redis对订单进行加锁。不过看你的描述,没涉及到分布式事务,这样的话就用楼上的解决方式:更新数据状态的时候加个乐观锁。

微笑丶永远 发表于 2022-11-1 19:32

沙发正解
没毛病
页: [1] 2
查看完整版本: mysql事务请教