![]() | 1 Aoang 2022-04-03 19:00:17 +08:00 via iPhone 事务呢?有事务不用,自己造个三角形的轮子 |
2 Jooooooooo 2022-04-03 19:01:20 +08:00 先用 redis 拦一遍是可以的 最终在 update 里加个 where 余额>100 的条件啊 |
![]() | 3 Features OP @Jooooooooo 哦哦,对,这样应该是可以的 |
![]() | 4 Features OP 实测是我太弱智了 应该先 update where 余额 >= 100 ,这个语句不成功就 trhow Exception 就好了 成功则添加记录等动作 |
img src="https://cdn.v2ex.com/avatar/6924/9f66/342244_normal.png?m=1678073553" class="avatar" border="0" align="default" alt="javapythongo" data-uid="342244" /> | 5 javapythongo 2022-04-03 19:21:45 +08:00 行锁、乐观锁吧 |
![]() | 6 broadliyn 2022-04-03 19:41:28 +08:00 update user set balance=balance-100 where id=1 and balance>=100; |
![]() | 7 xuanbg 2022-04-03 19:57:11 +08:00 6 楼正解 |
![]() | 8 kingjpa 2022-04-03 20:42:42 +08:00 我一般是 ,先锁,开启事务 然后更新, 后提交事务, 测过并发,没问题的。 其实 redis 也可以,但多一个服务就多一个不确定性。 |
9 sunnyandpenta 2022-04-03 20:49:07 +08:00 start transaction 1.SELECT balance FROM user where user_id = #{userId} for update; 2. int affectRows = update user set balance=balance-100 where user_id = #{userId} and balance>=100; 3. if(affectRows > 0){ commit; }else{ rollback; } |
10 stonewu 2022-04-03 21:21:02 +08:00 update user set balance = balance - 你要扣除的金额 where balance - 你要扣除的金额 >= 0 and id=用户 ID; |
![]() | 11 jasonkayzk 2022-04-03 21:25:43 +08:00 这么经典的问题,用乐观锁不就解决了: update user set balance=balance-? where id=? and balance>=?; |
![]() | 12 documentzhangx66 2022-04-03 21:33:35 +08:00 当你还不懂使用事务的情况下,这种涉及到金钱的操作,还是建议使用 Oracle 。 一来 Oracle 稳,第二是 Oracle 这种扣钱的案例,能搜到很多例子... |
13 iseki 2022-04-03 21:43:41 +08:00 如果你对事务不太了解,建议切换至 Serializable 隔离等级 |
14 kekxv 2022-04-03 21:44:16 +08:00 via iPhone 时间有要求?没有的话有个最简单的,直接用 redis 锁这个 ID 就行 |
15 iseki 2022-04-03 21:44:31 +08:00 哦,如果完全不会用事务,那啥隔离等级也没用这倒是忘了 |
![]() | 16 liangkang1436 2022-04-03 21:49:35 +08:00 via Android 你应该用事务和锁来实现,而不是用尽办法保证只有一个线程在更新数据库,因为数据库不是你一个人在用,别人也在可能操作这个表 |
17 512357301 2022-04-03 21:50:53 +08:00 via Android 6 楼正解。 还有就是楼主很明显在用代码的栈或者数组思维搞数据库,却忘了数据库最擅长增删改查了,尤其是 MySQL |
![]() | 18 liangkang1436 2022-04-03 21:51:25 +08:00 via Android 我记得 MySQL 默认的事务级别就是可重复读 |
![]() | 19 Features OP @liangkang1436 确实,是我刚学这个,遇到的问题太初级了 |
![]() | 20 Features OP @Aoang @iseki @liangkang1436 请教下,这种高并发的情况下,和事务有关系吗? 前一个请求中有未执行完成的事务,后面的 N 个请求又启用了 N 个事务 还是说 Mysql 的机制中只允许存在一个事务? |
![]() | 21 liangkang1436 2022-04-03 22:09:24 +08:00 via Android @Features 你先去谷歌一下事务的基础知识,比如什么是 acid ,然后再去查一下 MySQL 的事务相关的配置和 SQL 写法,然后你就知道怎么走了 |
![]() | 22 CEBBCAT 2022-04-04 01:39:34 +08:00 你的学习方式是不是有问题?上次的问题 /t/844048 就已经在 MySQL 工作原理上大错特错了,这个帖子又是一样地,只讲问题,只求解决,不讲原理 强烈建议你找一本正儿八经的,偏实战方向的书学习一下。等下我可以帮你找一些 很抱歉,最近精力不足,不能更 friendly 地回复你的帖子。 如果你有问题,请先去 Google 过再来问!请不要把 BBS 当成可以随便发言的 IM 聊天软件。请阅读《提问的智慧》。 根据我的视角,你总是拿一些初学者教程就会教授的东西来问,这不必要 |
![]() | 23 CEBBCAT 2022-04-04 01:56:56 +08:00 @CEBBCAT 你可以找找看《疯狂 Java 讲义》,我没有读过它,但我看到他的目录里面涉及了 MySQL 以及对应的事务部分。另外对于初学者来说通过培训班的视频学习也是一种方式,而且也是更为直观的方式。 假如你能耐得下心,以及有时间,去按照传统的计算机原理、操作系统、数据结构去学习,那再好不过了 |
![]() | 24 Evilk 2022-04-04 15:17:24 +08:00 via iPhone 如果是 mysql 先开启事务 根据主键 ID 锁住这行数据 再进行相关操作 最后再提交事务 我最近正好在做这种需求 测试过并发 没有出现超扣的情况 我之前也像你一样,从其他方面来做 却忘了数据库本身就很擅长这种业务 |
![]() | 26 huangzhe8263 2022-04-04 18:42:06 +08:00 这种入门级的问题,还是好好补一下基础好。 现在还是能发现 bug 的,万一往里头埋了啥大漏洞进去 那就不是,来论坛问,能解决的了 |
27 iseki 2022-04-04 23:08:25 +08:00 ![]() @Features 事务虽然会并发执行,但会按照设定的隔离等级,利用锁和多版本记录等机制保证正确处理你面对的这种问题,最严格的 Serializable (可序列化)隔离等级理论上会保证执行结果和看起来一个一个非并发的执行完全一致(当然只是理论上,实际中不同的数据库在一些细节上还是会有些不同的偏差,需要查阅数据库文档,不过感觉你现在不需要关心这些)。 具体比如如下这个例子: ```sql select 账户 ID, 余额 from table_name where 余额 >= 100; update table_name set 余额 = xxxxx where 账户 ID = xxx; ``` 在( PostgreSQL 数据库中)隔离等级为 RR 或 SI 时,慢一步的会话可能会在 update 时报错,事务 rollback 。MySQL 在 RR 下不会报错失败(Lost update) 但在 SI 下也会不能提交。 (所以 PostgreSQL 好!:) |
28 veightz 2022-04-05 01:55:56 +08:00 via iPhone 一般来说,改余额还要配合流水表的,直接一张余额表该值,后面会很痛苦…. |