天天看点

事务,分布式事务事务分布式事务分布式事务开源框架

  我们开发一个业务的时候,涉及到多个sql语句操作,为了保证此业务的正常运行,该业务的sql组合,要么都成功,要么都失败,失败表示整个过程都没有意义,数据库回滚到之前没操作的初始状态,就跟没操作一样,这种特性被称为事务

事务和分布式组合浅谈

事务

四大特性(ACID):

  1. 原子性[atomicity]:功能不可再分,要么全部成功,要么全部失败。
  2. 一致性[consistency]:事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。
  3. 隔离性[isolation]:隔离性是并发访问数据库时,操作同一张表时,数据库为每一个线程开启的事务,各事务不会互相影响,在该事务执行的过程中,无论发生的任何数据的改变都应该只存在于该事务之中,对外界不存在任何影响。
  4. 持久性[durability]:是事务的保证,事务终结的标志(内存的数据持久到硬盘文件中),当事务正确完成后,它对于数据的改变是永久性的。

并发事务导致的问题:

  1. 脏读(Dirty Read): 脏读是指在一个事务处理过程里读取了另一个未提交的事务中的数据。例如你账号中有 100元,A向你转了1元钱,但是事务没有提交,你又可以看到自己账户上确实有101元(产生脏读),去买一个101元的东西却被告知余额不足。
  2. 不可重复读(Unrepeatable Read): 同一查询在同一事务中多次进行,由于其他提交事务所做的修改或删除,每次返回不同的结果集,此时发生不可重复读。 你老婆查到自己账户上有100元准备买件衣服,你在你老婆要付款的时候,瞬间买了一包烟,这时你老婆一看余额就傻眼了,然她当然想着怎么收拾你才好。
  3. 幻读(Phantom Reads):事务在操作过程中进行两次查询,第二次查询的结果包含了第一次查询中未出现的数据(这里并不要求两次查询的SQL语句相同)。这是因为在两次查询过程中有另外一个事务插入数据造成的。你老婆收拾家里面所有的脏衣服准备洗了,刚收拾完,你又换了一套脏衣服,你老婆这时肯定会想"我不是把脏衣服都收拾完了吗?怎么还有一套?是幻觉吗?"。
  4. 丢失修改(Lost Update):第一类:当两个事务更新相同的数据源,如果第一个事务被提交,第二个却被撤销,那么连同第一个事务做的更新也被撤销。 第二类:有两个并发事务同时读取同一行数据,然后其中一个对它进行修改提交,而另一个也进行了修改提交。这就会造成第一次写操作失效。

怎样处理事务并发问题:

jdbc解决方案

① Serializable (串行化):可避免脏读、不可重复读、幻读的发生。

② Repeatable read (可重复读):可避免脏读、不可重复读的发生。

③ Read committed (读已提交):可避免脏读的发生。

④ Read uncommitted (读未提交):最低级别,任何情况都无法保证。
           

Spring解决方案

① PROPAGATION_REQUIRED: 支持当前事务,如果当前没有事务,就新建一个事务

② RROPAGATION_REQUIRES_NEW:创建一个新事务,如果当前存在事务,将这个事务挂起。也就是说如果当前存在事务,那么将当前的事务挂起,并开启一个新事务去执行REQUIRES_NEW标志的方法。

③ PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。

④ PROPAGATION_SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。

⑤ PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。

⑥ PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。

⑦ PROPAGATION_MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
           
事务,分布式事务事务分布式事务分布式事务开源框架

分布式事务

分布式事务就是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。分布式系统中,一个功能是多个子模块协同组成的,那如果某一个子模块执行不成功,本地事务可以回滚,那其他子模块怎么回滚?

分布式事务解决方案

2PC

两阶段提交(Two-phase Commit,2PC),通过引入协调者(Coordinator)来协调参与者的行为,并最终决定这些参与者是否要真正执行事务。

事务,分布式事务事务分布式事务分布式事务开源框架

缺点

  1. 同步阻塞 所有事务参与者在等待其它参与者响应的时候都处于同步阻塞状态,无法进行其它操作。
  2. 单点问题 协调者在 2PC 中起到非常大的作用,发生故障将会造成很大影响。特别是在阶段二发生故障,所有参与者会一直等待状态,无法完成其它操作。
  3. 数据不一致 在阶段二,如果协调者只发送了部分 Commit 消息,此时网络发生异常,那么只有部分参与者接收到 Commit 消息,也就是说只有部分参与者提交了事务,使得系统数据不一致。
  4. 太过保守 任意一个节点失败就会导致整个事务失败,没有完善的容错机制。

TCC

TCC 指的是Try - Confirm - Cancel。 Try 指的是预留,即资源的预留和锁定,注意是预留。 Confirm 指的是确认操作,这一步其实就是真正的执行了。 Cancel 指的是撤销操作,可以理解为把预留阶段的动作撤销了。 其实从思想上看和 2PC 差不多,都是先试探性的执行,如果都可以那就真正的执行,如果不行就回滚。 比如说一个事务要执行A、B、C三个操作,那么先对三个操作执行预留动作。如果都预留成功了那么就执行确认操作,如果有一个预留失败那就都执行撤销动作。

事务,分布式事务事务分布式事务分布式事务开源框架

缺点

因为事务状态管理,将产生多次DB操作,这将损耗一定的性能,并使得整个TCC事务时间拉长。 事务涉及方越多,Try、Confirm、Cancel中的代码就越复杂,可复用性就越底(这一点主要是相对最终一致性方案而言的)。另外涉及方越多,这几个阶段的处理时间越长,失败的可能性也越高。

本地消息表

本地消息表其实就是利用了 各系统本地的事务来实现分布式事务。 本地消息表顾名思义就是会有一张存放本地消息的表,一般都是放在数据库中,然后在执行业务的时候 将业务的执行和将消息放入消息表中的操作放在同一个事务中,这样就能保证消息放入本地表中业务肯定是执行成功的。 然后再去调用下一个操作,如果下一个操作调用成功了好说,消息表的消息状态可以直接改成已成功。 如果调用失败也没事,会有 后台任务定时去读取本地消息表,筛选出还未成功的消息再调用对应的服务,服务更新成功了再变更消息的状态。 这时候有可能消息对应的操作不成功,因此也需要重试,重试就得保证对应服务的方法是幂等的,而且一般重试会有最大次数,超过最大次数可以记录下报警让人工处理。 可以看到本地消息表其实实现的是最终一致性,容忍了数据暂时不一致的情况。

事务,分布式事务事务分布式事务分布式事务开源框架

消息事务

RocketMQ 就很好的支持了消息事务,让我们来看一下如何通过消息实现事务。 第一步先给 Broker 发送事务消息即半消息,半消息不是说一半消息,而是这个消息对消费者来说不可见,然后发送成功后发送方再执行本地事务。 再根据本地事务的结果向 Broker 发送 Commit 或者 RollBack 命令。 并且 RocketMQ 的发送方会提供一个反查事务状态接口,如果一段时间内半消息没有收到任何操作请求,那么 Broker 会通过反查接口得知发送方事务是否执行成功,然后执行 Commit 或者 RollBack 命令。 如果是 Commit 那么订阅方就能收到这条消息,然后再做对应的操作,做完了之后再消费这条消息即可。 如果是 RollBack 那么订阅方收不到这条消息,等于事务就没执行过。 可以看到通过 RocketMQ 还是比较容易实现的,RocketMQ 提供了事务消息的功能,我们只需要定义好事务反查接口即可。

分布式事务开源框架

hmily seata myth ........

继续阅读