微服务系统
事务
事务的原则
事务:是一种可靠,一致的方式,访问和操作数据库中数据的程序单元
原则特性: 原子性, 一致性 ,隔离性:事务之间互不干扰,持久性: 没提交事务之前,系统挂了,数据还是之前的数据,并没有被改变
mysql 查询事务的隔离级别
SELECT @@GLOBAL.tx_isolation,@@tx_isolation;
默认是 REPEATABLE-READ 可重复读
什么是 可重复读?
比如 开启一个 事务,这个事务只读某一个表的 某条 的取数据。
但是这时候并没有提交 , 但是 这时候另一个事务 修改了 该条数据,并提交了事务。
此时 之前的 的那个为 提交的事务,再次查询数据,这时候的数据是 未改变之前的数据,并不会因为 其他事务修改了数据提交了事务,
而显示出来 修改的值。
mysql 可重复读 修复
因为mysql 的事务隔离级别是 可重复读,另一个事务a 在执行更新数据,但是未提交, 这时候 一个事务 读取到 值之后, 另一个事务a更新了数据,
那么这时候 之前读取的值就不对了,怎么解决呢?
1, 要么改 数据隔离级别是 序列化的, 可以修改当前 连接 session的 事务隔离级别
2. 使用 FOR UPDATE
这时候 比如
SELECT * FROM T_USER FOR UPDATE 此时 如果该表有未 提交事务,将等待 事务提交完成,才会去读取的
注意 FOR UPDATE 要加上 主键 作为条件过滤或者是 唯一索引, 否则会锁全表,而不是 只锁一行数据
SELECT * FROM T_USER where id=11 FOR UPDATE
spring 事务机制
事务管理
事务抽象
PlatformTransactionManager 事务管理器接口, 事务的开启,提交,回滚等
TransactionDefinition 事务定义接口,提供传播熟悉,隔离属性等创建事务的定义
TransactionStatus 事务正在运行的状态
默认spring的事务隔离机制和 数据库的事务隔离级别一致
实现
transaction 注解 方式 使用,
JmsTransactionManager 即 MQ消息队列的相关事务处理
JMS 事务实例
https://blog.csdn.net/u014401141/article/details/54772847
https://www.cnblogs.com/vipstone/p/9350075.html
https://www.jianshu.com/p/1ee6be549fda
https://www.cnblogs.com/liuyuan1227/p/10776384.html
spring 外部事务与JTA
即第一阶段 某些提交有错误,那么都去 执行rollback
即一个服务中使用2个数据源
也不能在一个事务里面管理多个数据库的connection
所有分布式事务,最基本问题是 一个服务访问 多个数据源的问题
XA
xa : 分布式协议
JTA是 JAVA中对 XA规范的实现
xid 即 全局事务id
以上的方式 使用 spring 事务同步机制,但是在最后一个 事务出错的情况下, 要之前的事务都回滚还是 做不到的
实例
分布式系统
分区容错性: 多机部署
可用性: 负载均衡,多实例
一致性: 数据最终一致性
实现一致性
强一致性(不可能的)
弱一致性,
最终一致性(实现容易,性能高,开发维护比较低)
分布式表现形式
1, 多数据源,
2.多服务
3, SOA
4, 微服务架构
分布式考虑的问题
1, 服务分拆
2, 数据分拆
3, 计算分拆
4, 服务状态以及异常处理
微服务组成
1,服务发现与注册
2, 服务网关与负载均衡
3, 监控与熔断机制
4, 配置,消息等
分布式事务
定义: 在分布式系统中,实现事务
spring 分布式事务 JTA 实现
但是JTA 性能比较差,处理速度也比较慢, 在分布式中,结合MQ的话,就是 事务表现为 弱一致性或者 最终一致性的
JTA 是两阶段提交,先是预提交,然后都没有问题,才会都提交,所以事务 耗时。
这时候,当 消息事务提交出错了, 数据库事务并不能 回滚, 就要解决这个问题了。
也就是 将 数据库 的提交分为2阶段了, 避免了, 消息 事务的提交失败而 回滚不了 数据库的事务了
但是最终 数据库事务 提交事务了,消息 事务是回滚不了的。 可以极大的避免出错概率
这就是 使用同一个事务提交 MQ事务,和 数据库的表的事务提交
即提交第6步出错了,所以就重试了, 会重复操作数据库, 这时候要处理好幂等了
也就是 如果处理 失败了,那就就需要 写一个 处理失败的消息队列,去处理失败的回滚操作
链式事务管理
即spring为多数据源提高的 事务解决方案
@Bean public PlatformTransactionManager transactionManager() { DataSourceTransactionManager userTM = new DataSourceTransactionManager(userDataSource()); PlatformTransactionManager orderTM = new DataSourceTransactionManager(orderDataSource()); //链式的事务管理就是这里加进去 ChainedTransactionManager tm = new ChainedTransactionManager(userTM, orderTM); return tm; }
链式的数据不一致的情况是: 比如 两个数据库, 在commit 第一个数据库的 事务之后, 开始 commit 第二个数据库的事务的时候,第二个数据库挂了。
那么 这时候,就发现 第一个数据库 有数据了,而第二个数据库没有数据, 这就造成了 数据不一致了。
和第一个实例的情况是一样
总之就是 可能第二个事务提交 出现异常之后, 第一个已提交的事务是没法回滚的, 所以才需要 做好补偿。
其他情况都是没有问题的,效率也是不低的。其他情况都可以满足事务的要求。
这个实例也是可以用 链式事务 去做 数据同步的。
也就是都会一起 同步提交事务。 同理和 链式 事务也是一样的, 比如 提交最好一个事务,最后一个事务是MQ事务,这时候MQ挂了,但是 数据库已经提交事务了。 也就数据不一致了。也是要做好补偿.
分布式事务实现模式
幂等
唯一ID
分布式对象
将 全局的唯一id 放入一个 分布式对象里面,这样就可以通过查询 判断是否 已经存在了
分布式事务问题
注意的问题
支持事务,一般MQ都支持,关键要和spring 整合在一起。
一般都是使用定时任务去处理
实践例子
1. 如果MQ支持事务,并且 支持能够和spring 很好的整合在一起的话,
比如spring 的链式事务 或者 最大努力一次提交事务 这样。
就可以 将 MQ事务与 数据库的事务整合在一起,做到同步 提交事务。
这时候就要保证 提交事务的顺序性,要保证数据库的事务提交,然后才到MQ的事务提交。否则可能 MQ事务提交了,但是DB 事务提交失败(比如数据库挂了等情况),就会容易造成 数据不一致了。虽然这种情况可能导致数据库有数据库,但是MQ消息提交失败,这时候可以使用定时器 处理重发到MQ里面即可。
2. 一些MQ可能不支持事务,或者说 开启MQ事务会性能很差,
比如rabbitMQ 开启事务,性能就比较差,但是也有 相应的保证消息100%投递的措施,也不能排除消息没有投递的可能(比如极端情况,投递时候MQ挂了)。
如果MQ不开启事务的话:
1, 处理好 如果数据库没有数据,而MQ发了消息的数据不一致情况
2, 数据库有数据了, MQ没有发消息的数据不一致(定时器可以解决)
解决的方式
最终都是要保证数据一致性即可
1,不管怎样都需要一个定时器,定时去扫描 比如订单表里面, 扫描那些 待处理的订单。
在一定时间内还没有被处理,这时候就可以 去 查询 整个订单消息流程是那部分出错了。
比如 查询到user 服务,并没有 相关订单的扣费,那就是 user服务并没有收到 order的MQ消息了,重发即可。
或者说待处理订单超时严重,触发 订单失败相关的流程处理。
2. 幂等肯定是要 进行 保障的, 每个服务都需要判断是否 已经处理了,防止消息重发的时候,造成的幂等问题。
当然了 为了防止 MQ消息发送 了, 但是 数据库并没有数据的情况:
比如 订单服务并没有创建待处理订单数据到数据库,而MQ发了消息给user服务去处理。
User服务处理的时候 就要 查询 订单服务订单数据,判断 订单服务是否有该 订单数据了,没有就不处理该MQ 消息即可。
3. 整个MQ 消息处理流程,每次处理的都需要尽量校验,比如 校验 源头的 数据状态是否正常,
比如 处理到 用户扣费的时候, 要校验一下订单状态是否正确, 可能是订单状态 已经被 改为 失败了,失败原因可能是 订单处理超时了,
然后订单 那边设置为 订单失败了, 如果不去 再次查询确认订单状态,那么 用户服务这边就在处理MQ订单扣费消息的时候,去扣费了。
这就数据不一致了。这样多重检查来极大减少出错。
但是也可能出现 订单超时设置失败,而同时用户服务那边同时扣费了,
那么用户服务这边就需要来一个定时任务检查 因为订单而扣费是否合理,如果不合理(订单扣费了,但是订单超时失败了)
就要将 用户余额给 回滚 回去。当然这个 用户余额扣费肯定要做好 幂等。
因此如果是特别复杂的处理的时候, 最大的兜底应该是 每个MQ流程的每个微服务都可以加上对应的定时任务,去检查 处理是否正常。对应进行补偿或者是重试。
分布式事务实现-事件溯源
实例
数据流处理: 即可以 提供 数据分析
性能很高效
操作都是异步的, 所以只能是一致性
Event Store 的分布式扩展性和微服务的扩展是不一样的,也是难点点
即 对应的 服务处理之后,然后生成对应的事件,然后继续处理
即一端是 执行命令,一端去查询,就可以提高并行处理
事件溯源与Axon框架
TCC模式,分布式事务
总结
使用消息的方式解决分布式事务,实现比较容易而且灵活
微服务架构设计模式
数据共享模式,可以说是 微服务架构的 临时的实现方案
预留资源:比如 下单的时候, 订单状态为 下单中,待处理订单 这样的中间状态,来预留 资源