mysql的三种日志
1、undo log
为了满足事务的原子性,在操作任何数据之前,首先将数据备份到Undo Log。然后进行数据的修改。如果出现了错误或者用户执行了ROLLBACK语句,系统可以利用Undo Log中的备份将数据恢复到事务开始之前的状态。
2、redo log
只是记录了数据发生了那些修改的日志,并且是物理的修改,事务提交之前,会把修改的内容写入到redo log中,如事务提交之后,某页发生了什么内容的修改,当提交的时候,就可以直接持久化redo 日志当中的内容,从而可以减少io。
3、bin log日志
它会把所有sql的执行过程都保存在该日志当中,
主从复制 ,主主复制就是通过bin log日志 实现。
分布式事务的解决方式
一、事后对账
比如,上图一个扣库存的服务,另一个扣钱的服务,在事后发现只有一个扣成功,那么就代表该订单的事务是失败的,所以其中成功的那个也应该回滚。
二、rabbitmq中间件
如主控服务 订单作为生产者服务A 执行完成事务 并且完成事务的提交,之后, 发出消息 给到rabbitmq ,让其他服务可以进行消费动作,如扣款服务,那可以通过到mq当中接收消息来消费。业务失败,可以通过 消费端开启手动确认消费,业务失败则消息重回队列的方式来保证 最终一致。
保证消息的可靠性,可以增加数据库,将发送消息的指令保存到数据库当中,这样多了一个保险,就是在消费者消费完成之后,再去数据库中,对该条信息完成 最终的消费的状态修改,能够协同两个服务A和B
不是强一致,但是是最终一致的。
三、二阶段提交
如上图,第一个阶段 所有的微服务完成自己的事务,但是不提交,并且把自己事务是否成功的消息告诉协调者,协调者来在第二个阶段决定事务是否提交。
缺点:当第二个阶段执行的服务挂掉,那么服务则无法继续执行提交或者回滚的操作
因为第一个阶段的操作需要等待第二个阶段的响应才能完成,在此期间,会保持资源锁定,其他线程不可获取,导致性能受影响。
TCC try confirm cancel
2pc 就是指上面刚说到的二阶段提交的方式,也就是在二阶段提交的方式上面做了优化。
上图是关于 TCC的实例,是一个 服务从A账户完成扣款,然后另一个服务扣库存。这里只拿扣款服务举例,扣库存的服务类似。
第一阶段:需要把A账户进行扣款,需要做的是,在账户表增加一个冻结部分的字段,将其设置为30,然后该服务就可以完成提交了。
第二阶段,
如果第一阶段的所有服务的事务,都执行成功,那么需要提交,就是把余额扣除30,并且把冻结部分的钱置为0。
如果第一阶段的服务有事务失败了,那么就只需要把冻结部分的前置为0即可。
优点:解决了 二阶段提交,资源锁的占用问题,所有阶段都是独立的事务,互不影响。
缺点:每一个微服务 都需要写 try confirm cancel 的代码,代码侵入严重。
seata
seata 也是两个阶段,并且两个阶段也是独立的,第一个阶段执行完毕之后就直接提交了,然后向TC发送自己的执行状态。
第一个阶段虽然是完成了提交,但是会把事务执行之前的数据状态写在undo log 中,会把数据的修改状态写在redo log中。
二阶段如果是提交的话,因为“业务 SQL
”在一阶段已经提交至数据库, 所以 Seata 框架只需将一阶段保存的快照数据和行锁删掉,完成数据清理即可。
二阶段如果是回滚的话,Seata 就需要回滚一阶段已经执行的“业务 SQL
”,还原业务数据。回滚方式便是用“before image
”还原业务数据(也就是利用undo log 回滚);但在还原前要首先要校验脏写,对比“数据库当前业务数据”和 “after image
”,如果两份数据完全一致就说明没有脏写,可以还原业务数据,如果不一致就说明有脏写
,出现脏写就需要转人工处理。
优点:
相较与二阶段提交,seata 每个事务都是单独提交的,事务执行完就在本地提交了,不需要等待和资源锁定。
相较于TCC的方式,seata 二阶段的提交和回滚事务,都是由框架完成的,通过类似于undo log 和redo log 完成事务的提交和回滚的操作。