java mysql分布式事务_Java分布式事务-转载

举个例子。假设系统中有以下两个表

user(id, name, amt_sold, amt_bought)

transaction(xid, seller_id, buyer_id, amount)

其中user表记录用户交易汇总信息,transaction表记录每个交易的详细信息。

这样,在进行一笔交易时,若使用事务,就需要对数据库进行以下操作:

begin;

INSERT INTO transaction

VALUES(xid, $seller_id, $buyer_id, $amount);

UPDATE user SET amt_sold =

amt_sold + $amount WHERE id = $seller_id;

UPDATE user SET amt_bought =

amt_bought + $amount WHERE id = $buyer_id;

commit;

即在transaction表中记录交易信息,然后更新卖家和买家的状态。

假设transaction表和user表存储在不同的节点上,那么上述事务就是一个分布式事务。要消除这一分布式事务,将它拆分成两个子事务,一

个更新transaction表,一个更新user表是不行的,因为有可能transaction表更新成功后,更新user失败,系统将不能恢复到一致

状态。

解决方案是使用消息队列。如下所示,先启动一个事务,更新transaction表后,并不直接去更新user表,而是将要对user表进行的更新插入到消息队列中。另外有一个异步任务轮询队列内容进行处理。

begin;

INSERT INTO transaction

VALUES(xid, $seller_id, $buyer_id, $amount);

put_to_queue “update

user(“seller”, $seller_id, amount);

put_to_queue “update

user(“buyer”, $buyer_id, amount);

commit;

for each message in

queue

begin;

dequeue message;

if message.type = “seller”

then

UPDATE user SET amt_sold =

amt_sold + message.amount WHERE id =

message.user_id;

else

UPDATE user SET amt_bought =

amt_bought + message.amount WHERE id =

message.user_id;

end

commit;

end

上述解决方案看似完美,实际上还没有解决分布式问题。为了使第一个事务不涉及分布式操作,消息队列必须与transaction表使用同一套存储资源,但为了使第二个事务是本地的,消息队列存储又必须与user表在一起。这两者是不可能同时满足的。

如果消息具有操作幂等性,也就是一个消息被应用多次与应用一次产生的效果是一样的话,上述问题是很好解决的,只要将消息队列放到

transaction表一起,然后在第二个事务中,先应用消息,再从消息队列中删除。由于消息队列存储与user表不在一起,应用消息后,可能还没来得

及将应用过的消息从队列中删除时系统就出故障了。这时系统恢复后会重新应用一次这一消息,由于幂等性,应用多次也能产生正确的结果。

但实际情况下,消息很难具有幂等性,比如上述的UPDATE操作,执行一次和执行多次的结束显然是不一样的。解决这一问题的方法是使用另一个表记录

已经被成功应用的消息,并且这个表使用与user表相同的存储。假设增加以下表

message_applied(msg_id)记录被成功应用的消息,则产生最终的解决方案如下:

begin;

INSERT INTO transaction

VALUES(xid, $seller_id, $buyer_id, $amount);

put_to_queue “update

user(“seller”, $seller_id, amount);

put_to_queue “update

user(“buyer”, $buyer_id, amount);

commit;

for each message in

queue

begin;

SELECT count(*) as cnt FROM

message_applied WHERE msg_id = message.id;

if cnt = 0 then

if message.type = “seller”

then

UPDATE user SET amt_sold =

amt_sold + message.amount WHERE id =

message.user_id;

else

UPDATE user SET amt_bought =

amt_bought + message.amount WHERE id =

message.user_id;

end

INSERT INTO message_applied

VALUES(message.id);

end

commit;

if 上述事务成功

dequeue message

DELETE FROM message_applied WHERE

msg_id = message.id;

end

end

我们来仔细分析一下:

1、消息队列与transaction使用同一实例,因此第一个事务不涉及分布式操作;

2、message_applied与user表在同一个实例中,也能保证一致性;

3、第二个事务结束后,dequeue

message之前系统可能出故障,出故障后系统会重新从消息队列中取出这一消息,但通过message_applied表可以检查出来这一消息已经被应用过,跳过这一消息实现正确的行为;

4、最后将已经成功应用,且已经从消息队列中删除的消息从message_applied表中删除,可以将message_applied表保证在很小的

状态(不清除也是可以的,不影响系统正确性)。由于消息队列与message_applied在不同实例上,dequeue

message之后,将对应message_applied记录删除之前可能出故障。一但这时出现故障,message_applied表中会留下一些垃

圾内容,但不影响系统正确性,另外这些垃圾内容也是可以正确清理的。虽然由于没有分布式事务的强一致性保证,使用上述方案在系统发生故障时,系统将短时间内处于不一致状态。但基于消息队列和消息应用状态表,最终可以将系统恢复到一致。使用消息队列方案,解除了两个数据库实例之间的紧密耦合,其性能和可伸缩性是分布式事务不可比拟的。

当然,使用分布式事务有助于简化应用开发,使用消息队列明显需要更多的工作量,两者各有优缺点。个人观点是,对于时间紧迫或者对性能要求不高的系

统,应采用分布式事务加快开发效率,对于时间需求不是很紧,对性能要求很高的系统,应考虑使用消息队列方案。对于原使用分布式事务,且系统已趋于稳定,性能要求高的系统,则可以使用消息队列方案进行重构来优化性能。

注: 本文取材于eBay的工程师Dan Pritchet写的这篇文章

,并转载至http://wangyuanzju.blog.163.com/blog/static/1302920086424341932

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Java中的分布式事务指的是在分布式系统中对多个数据库或服务进行操作时,保证数据一致性和事务的原子性。Java提供了多种方式来实现分布式事务,下面介绍几种常见的方式: 1. 两阶段提交(2PC):在分布式事务中,协调者负责协调所有参与者的操作,通过两个阶段来实现事务的提交。第一阶段,协调者询问所有参与者是否可以提交事务,参与者将结果反馈给协调者。第二阶段,协调者根据参与者的反馈决定是否提交或回滚事务。 2. 补偿事务(TCC):TCC是一种基于补偿机制的分布式事务解决方案。它将一个大的事务拆分为多个小的事务,并为每个小事务定义了Try、Confirm和Cancel三个阶段。通过执行相应的补偿操作,可以实现最终的一致性。 3. 消息队列:使用消息队列作为分布式事务的中间件,将事务操作封装成消息发送到队列中,并由消费者进行处理。当所有操作都成功完成时,提交事务;如果有一个或多个操作失败,则回滚事务。 4. 分布式数据库:通过使用支持分布式事务的数据库,如MySQL Cluster、PostgreSQL等,可以实现跨多个数据库的事务操作。这些数据库提供了事务一致性和隔离级别的保证。 需要注意的是,分布式事务在实现过程中需要考虑网络延迟、单点故障、数据冲突等问题,因此在设计分布式系统时需谨慎权衡各种方案,并根据具体业务需求选择适合的分布式事务解决方案。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值