分布式事务
我要是一条狗 这块硬骨头 理都不想理,但是谁让我是一名程序员
本地消息表:说白了就是一个记录
操作信息 存到库中 成功状态改为成功,失败定时任务重试调用
消息队列:rocketMQ
半事务的典范,mq中的战斗机!
1.生产者订单系统发送一条half消息到broker
2.创建订单,成功与否 向broker发送commit和rollback
3.向broker提供回调接口,当broker一段时间内half消息没有收到操作命令,则主动调此接口来查询订单是否创建成功
4.half消息commit了,另一个系统来消费,消费成功 消息销毁 分布式事务成功,上一篇有图可see
5.失败则重试策略 再失败 死信队列 等待进一步处理
seata:二阶段提交
他来了她来了 高度警惕 over over
微服务框架/at模式/tcc模式/saga模式/xa模式/高可用 Apache Seata
xa 强一致:acid 两阶段
无侵入,seata拦截并解析用户sql,保存其变更前后的数据镜像undo_log,自动生成事务提交/回滚
rm 本地事务隔离行保证全局隔离,串行化隔离级别下性能问题
tm申请全局锁 tc 生成xid,tm通过微服务调用链传给其他服务
rm本地事务xid分支事务注册到tc ,tc返回说准备吧 rm执行本地事务并告知tc结果
tm请求tc对xid提交或回滚
tc指挥xid所有分支提交 回滚
回滚:回滚日志
分支事务对应数据库一个回滚日志undo_log,收到回滚据undo生成回滚操作
收到提交,undo_log响应记录删除掉
rm和tc
监控拦截jdbc,监控到开启了本地事务 自动向tc注册 生成回滚日志 汇报执行结果
二阶段回滚失败
tc命令rm回滚,一个微服务挂掉了,all微服务不执行回滚,直到正常启动,tc重新执行全局回滚
AT 锁缩短 两阶段
加锁 快照
基于xa,两阶段提交,锁时间缩短;隔离级别:读未提交
拦截解析sql,生成before image 执行sql 保存成after image,生成行锁,image组成undolog,数据更新 undolog放到同一个事务提交 释放本地锁定的资源
脏读
全局锁,tc记录 可访问其他字段 lock_table
TCC 三阶段提交
人工编码try confirm cancel接口,实现数据恢复
try资源的检测和预留,修改某个资源 先进行预留
confirm 业务执行和提交,完成操作业务,try成功confirm一定能成功
cancel预留资源释放,try的反向操作
空回滚和业务悬挂:就是try阻塞了先执行的cancel然后再try
加一个表记录try和cancel情况,未try不canel
微服务18_分布式事务02:Seatda:四种模式XA、AT、TCC、SAGA_分布式事务 at、tcc、saga 和 xa-CSDN博客
Saga
第二阶段 提交rm不需要处理,回滚执行业务补偿回滚方法
两阶段三阶段
基本完成sql语句操作未提交,都成功 通知all提交事务/回滚事务
canCommit是否正常,preCommit操作sql,doCommit提交/回滚事务
rm加超时机制解决阻塞
分布式锁
锁
所有进程都能访问到的地方设置锁资源,竞争锁资源
互斥/同一(持锁的删除)/可重入(续租)/容错(自动释放)
redis:单线程处理网络问题
setNx key value + expire key locktime 设置key有效时长
getset key value先get再set返回value,没有返回空再将key设置为value
lua脚本
redlock:多节点申请锁,一半以上节点获取成功算获取成功
redission用响应实现(aqs 看门狗 可重入 可续期)
redission分布式锁原理
获取锁:尝试获取两次,否则带过期时间阻塞等,无过期时间死等 (他个糟老锁子)
无锁超时时间且获取成功,看门狗定时任务scheduleExpirationRenewal自动续期:expiration_renewal_map中遍历判断是否存在
带过期时间的lock,不会启动看门狗任务
加锁
<T> RFuture<T> tryLockInnerAsync(long waitTime, long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {
/**
* key不存在第一次加锁,key的field存中可重入hincrby锁 续期pexpire
*/
return evalWriteAsync(getRawName(), LongCodec.INSTANCE, command,
"if ((redis.call('exists', KEYS[1]) == 0) " + // 1
"or (redis.call('hexists', KEYS[1], ARGV[2]) == 1)) then " + // 2
"redis.call('hincrby', KEYS[1], ARGV[2], 1); " + // 3
"redis.call('pexpire', KEYS[1], ARGV[1]); " + // 4
"return nil; " +
"end; " +
"return redis.call('pttl', KEYS[1]);", // 5
Collections.singletonList(getRawName()), unit.toMillis(leaseTime), getLockName(threadId));
}
可重入锁
加锁成功启动看门狗续期pexpire
锁释放
1.删除锁
2.广播释放锁的信息(redis.call('publish', KEYS[2], ARGV[1]))通知阻塞等待的进程(开始加锁时)
3.取消watchDog,redissonLock.expiration_renewal_map中的id删除,取消netty定时任务
zk
临时节点 有序 watch cp