能听的懂得消息队列

假设有两个账户A和B在独立的数据库上,如果A要向B转账100元,在一致性要求下,A账户-100,B账户+100,这两个操作要么全部做完,要么全部不做。但是在互联网的环境下(高并发),这是不大容易做到的。所以后来有了一个消息队列

还是之前的例子,A的账户转100出来,需要在数据库1发起一个事务,从A的账户扣100,还得向消息队列插入一条给B账户+100的信息,然后数据库1的事务就结束了。消息队列的信息在某一刻会被读取出来并执行,给B账户+100。为了防止消息队列宕机,消息队列中的数据都是持久化到硬盘的。这就是最终一致性。数据在某些时刻看起来不一致,虽然A账户的钱少了,但是B账户的钱没有变多,这时候“钱”在消息队列中暂存,等到数据处理完成,数据还是一致性的。在高并发的场景下,A账户扣完钱并向消息队列中插入消息,事务就结束了,不用两段性提交

两段提交:对于涉及多个分布式的数据库,有一个全局的事务管理器用来负责协调各个数据库的事务提交,大致分为两个阶段

1:全局的事务管理器向各个数据库发出消息,各个数据库需要在本地把一切都准备好,执行操作锁定资源,记录日志,但是并不提交。总而言之就是时刻进入准备提交或回滚的状态,然后向全局事务管理器报告:我准备好了;

2:如果所有得数据库都报告准备好了,那么全局的事务处理器就下命令:提交。这时,各个数据库才真正提交,由于之前都准备好了,这时候提交是可以很快速的完成的,如果有一个数据库报告没准备好,那么全局事务管理器就下令:放弃。这时候各个数据库就要进行回滚操作,并且释放在阶段1锁住的资源。

两段提交并不是很完美的,他有不少的问题 比如阶段2,全局事务处理器出了问题,各个数据库等着你下令,但你迟迟不下令。这时候大家都会阻塞,此时还锁着资源。又或者,事务管理器因为网络的原因,数据库1收到了 数据库2没收到,那就会造成数据不一致,涉及到金额的时候是很严重的问题。

需要注意的是:消息队列是通过一张事件表来插入记录的。A向B转账 可以分解为 

1:扣除A账户钱

2:向事件表中插入一条记录,记录向某个用户转入多少

事务提交

定时运行程序查询事件表并讲查询到的数据写入消息队列

消息队列读取消息:

B账户加钱

事务提交

此时还有一个隐形的问题,比如定时运行程序读取了数据,向消息队列中写入了消息,还没来得及执行就崩溃了。等到定时任务重启了之后,又会再次读取,再次向消息列表写入消息,将会导致B账户多次加钱。

这里有一个概念:幂等性。大概意思就是你对一个数据进行操作的时候可以一直重复进行操作但不会影响数据。比如你对一个账户查询1000次 10000次都不会影响账户的金额。

转账不是一个幂等性操作,所以需要再增加一张表用来记录执行过的操作。在每次执行消息队列的时候都需要先查询一次操作表,如果没执行过就增加,如果执行过了就简单的抛弃这条信息。

不和别人一样,不复制只真正理解
原文地址:https://www.cnblogs.com/Vinlen/p/12758560.html