MySql 事务解决并发问题

2016-07-06 18:49:04 +08:00
 Weixiao0725

先描述一下场景: 假设我有一张表 orders(id, order_id, money, type), 每次我选择某一个订单的最后一笔进行消耗。例如,现在表里有一条记录(3, 777, 34.00, 1),我现在要消耗 2.00 ,先把原来的记录查出来并update成(3, 777, 34.00, 0), 然后insert一条新的记录(4, 777, 32.00, 1)。

现在如果如果我的服务部署在多台机器上,就有可能两个并发连接同时取到同一条记录,然后在这条记录上消耗。比如,现在两个连接同时取到记录(3, 777, 34.00, 1),然后具体的消耗代码我放在事务中做,但是仍会得到结果记录为

(3, 777, 34.00, 0)
(4, 777, 32.00, 1)
(5, 777, 32.00, 1)

而如果正常消耗的话应该为

(3, 777, 34.00, 0)
(4, 777, 32.00, 0)
(5, 777, 30.00, 1)

我的业务代码使用 java 写的,必须先查上次最后的记录,然后具体的消耗的时候,即 update 和 insert 语句放到事务 里做,用的 read committed 隔离级别。

给出一个 pseudocode in Java

last = getInfoByOrderId(orderid, type);
//...
// 使用 last 先进行一些业务判断,如果不满足一些条件就直接报错返回了
//...
TransactionTemplate.execute(new TransactionCallback() {
    String sql = "select * from " + last.getTableName() + " where id = " + last.getId() + " for update";
    lastinfo = queryByIdForUpdate(sql);
    update(lastinfo);
    insertOneItem(consumeMoney);
        

});

业务场景有点复杂,望见谅:D ,对于这种并发场景,有没有好的处理方式?

5734 次点击
所在节点    MySQL
24 条回复
icegreen
2016-07-07 00:18:34 +08:00
1. 加锁,上面的都提到了.
2. 乐观锁, update 语句加一个 where 条件 type=1; 每次更新的时候, 判断 update 语句影响的条数, 如果更新了 0 条, 抛异常, 回滚事务; 如果更新一条记录, 成功;
phttc
2016-07-07 08:46:51 +08:00
好巧,,昨天我也遇到了类似问题。。
我的是这样的: UPDATE db_table SET value=value+1 WHERE id=1;
有没有方法在一句 sql 里把更新后的 value 值给 select 出来?考虑并发情况,要求取出的 value 不能被污染。。
最后采用的方法是先 select 出原始值。。然后 update 的时候加上判断,,根据返回影响的条数判断是不是被污染。
Weixiao0725
2016-07-07 09:27:29 +08:00
@phttc 其实,这个问题还是用分布式锁解决比较合适。我准备先用楼上说的 get_lock 试一下,不行的话就直接上 zookeeper 了
ihuotui
2016-07-11 01:25:52 +08:00
系统设计问题,同一用户的操作一定要保持在一个服务器上,要不然要设计复杂的分布式事物和同步

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://tanronggui.xyz/t/290706

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX