1
zacard 2016-02-15 10:31:05 +08:00
A 打款给 B 前就应该检查 B 是否超过上限,而不是先扣款
|
2
tabris17 OP @zacard 数据无法同步,这种检查意义不大,你无法保证『检查 B 账户余额』和『给 B 账户打款』的两个操作之间, B 账户余额不发生变化
|
3
wy315700 2016-02-15 10:46:19 +08:00
加锁
|
4
pelloz 2016-02-15 10:49:22 +08:00
排它锁?
|
5
noli 2016-02-15 10:49:59 +08:00 1
“ A 向 B 转账”(记为 T1 )的事务完成或者中止之前能够修改 A 帐户余额?
我不确定 T1 还能不能叫事务…… |
6
incompatible 2016-02-15 10:51:53 +08:00 via iPhone
|
7
EPr2hh6LADQWqRVH 2016-02-15 10:53:27 +08:00
Mongodb 不支持事务这件事写在 Cons 里面可不是白写的
请选用支持事务的数据库比如 postgresql , 或者自己实现软事务 |
8
EPr2hh6LADQWqRVH 2016-02-15 10:56:21 +08:00
|
9
tabris17 OP 能想到的是,在账户的 document 上增加一个锁标志字段,在对 document 操作前都手动检查一下锁标志位
|
10
tabris17 OP |
12
tabris17 OP @zacard
没用。 A 向 B 转账事务操作: 1. 检查 B 账户余额接受金额后是否会超出 100 的限额; 2. 检查通过则对 A 账户进行扣款; 3. 向 B 账户打款。 然而 1 和 3 操作之间,可能有其他的操作(比如 B 账户充值)会对 B 账户余额进行操作,第一步的检查白做 |
14
tabris17 OP @gy911201 阻塞的排他锁很麻烦,由于事务要分别获得 document A 和 document B 两个文档的锁,所以会造成死锁,还要自己实现死锁检测
|
15
Mirana 2016-02-15 11:38:59 +08:00
A 预提交了之后是会被锁住的
|
16
incompatible 2016-02-15 11:50:13 +08:00 via iPhone
@tabris17
抱歉,一开始没有看到是在 mongodb 节点下。 我的建议是: mongodb 完全不是为这种场景设计的,请考虑改用 mysql 或者 postgresql 来实现你的账户余额功能。这些支持 acid 的数据库很容易就可以支持你主贴中的业务场景,完全无需自己实现两阶段提交(事实上两阶段提交本来也不是用来干这个的,它通常用来实现多数据源的分布式事务) |
17
tabris17 OP @incompatible
确实,这个称作模拟事务日志比较恰当。 如果 mongo 要实现事务,除了模拟事务日志外,为了避免脏读,不可重复读,幻影行,还要实现 MVCC ,为了数据一致性,还要实现行(文档)锁、页锁、集合锁…… |
18
li24361 2016-02-15 13:14:01 +08:00
A->B 操作没完成之前, A 不能修改,否则不叫事务
|
19
breeswish 2016-02-15 13:44:46 +08:00
就这个例子而言,可以在 pending 后进行检查,如果检查失败则回滚( MongoDB 文档 Rollback Operations 下的 Transactions in Pending State )。
你担心检查完和修改记录之间有其他操作,这个和两阶段递交没关系,你想做的是确保一次只有一个事务占用资源。可以实现一个锁。对于这个问题而言,可以在查询中增加条件解决(显然能解决的问题不如锁那么多): db.accounts.update( { _id: t.destination, pendingTransactions: { $ne: t._id }, balance: { $lt: 100 - t.value} }, { $inc: { balance: t.value }, $push: { pendingTransactions: t._id } } ) 注意增加了一个 balance: { $lt: 100 - t.value} 条件。 update 失败( 0 affected )直接 rollback 即可 |
20
breeswish 2016-02-15 13:46:11 +08:00
(补:纯 MongoDB 的话可以利用 Tailable Cursor 实现锁
|
21
hantsy 2016-02-15 13:47:22 +08:00
看成 Two phase transcation 了。。。
|
22
zacard 2016-02-15 13:49:37 +08:00
@tabris17 1 和 3 操作之间如果有对余额修改(充值),要么会检查出此次充值失败,要么就会检查出这次转账失败。原子性来保证这一点。
|
24
palmers 2016-02-15 15:23:01 +08:00
能不能使用第三方账户转账呢? 优先检查 B 账户余额 满足立即转账否则直接退回, 第三方账户肯定可以控制不会在转账期余额被操作 这样可以吗?
|
25
tianice 2016-02-15 22:29:30 +08:00
mongo 支持原子操作,并不支持事务。也不能叫两阶段提交,两阶段提交数据库在第一次预提交成功之后,必须保证事务最终能提交或回滚。不能说预提交成功之后,让你回滚你滚不回去,让你提交提交不了,不能出现这两种情况,否则预提交没有意义。
|
26
tianice 2016-02-15 22:30:58 +08:00
涉及到钱的业务还是用关系型数据库吧,至少要保证事务一致
|
27
future0906 2016-02-16 12:41:15 +08:00
|