1.分布式锁及事务解决方案--探讨---可评论指正哦
1.1数据库乐观锁
1.1 利用表的唯一索引行级锁进行加解锁,加锁:
insert into methodLock(method_name,desc) values (‘method_name’,‘desc’)
释放锁,需要执行以下Sql:
delete from methodLock where method_name ='method_nam'
1.2 另外还可以使用数据库的排他锁的形式加锁,例如加上 for update:
result = select * from methodLock where method_name=xxx for update
在查询语句后面增加for update,数据库会在查询过程中给数据库表增加排他锁。
当某条记录被加上排他锁之后,其他线程无法再在该行记录上增加排他锁。
1.2基于Redis的分布式锁
jedis.setnx(String key, String value, String nxxx, String expx, int time)
1.第一个为key,我们使用key来当锁,因为key是唯一的。
2.第二个为value,我们传的是requestId,分布式锁要满足第四个条件解铃还须系铃人,通过给value赋值为requestId,我们就知道这把锁是哪个请求加的了,在解锁的时候就可以有依据。requestId可以使用UUID.randomUUID().toString()方法生成。
3.第三个为nxxx,这个参数我们填的是NX,意思是SET IF NOT EXIST,即当key不存在时,我们进行set操作;若key已经存在,则不做任何操作;
4.第四个为expx,这个参数我们传的是PX,意思是我们要给这个key加一个过期的设置,ex : seconds 秒 px : milliseconds 毫秒具体时间由第五个参数决定。
5.第五个为time,与第四个参数相呼应,代表key的过期时间。
1.3基于ZooKeeper的分布式锁
方式一:
利用名称唯一性
谁能创建某名称节点,谁就获得锁。但释放锁时会出现惊群效应,被PASS
方式二:
利用Zookeeper的EPHEMERAL_SEQUENTIAL类型节点及watcher机制,创建的节点都是有顺序的,每次选择最小编号的节点获得锁。
当前节点watcher上一个节点,当上一个节点被删除时,因为是临时节点,也就是上一个节点与zookeeper断开连接时,当前节点
成为最小节点,从而获得锁。
2.分布式事务解决方案
2.1 二阶段提交
2PC 引入一个事务协调者的角色来协调管理各参与者(也可称之为各本地资源)的提交和回滚,二阶段分别指的是准备(投票)和提交两个阶段。
2PC 是一种尽量保证强一致性的分布式事务,因此它是同步阻塞的,而同步阻塞就导致长久的资源锁定问题,总体而言效率低,并且存在单点故障问题,在极端条件下存在数据不一致的风险。
还有一点不知道你们看出来没,2PC 适用于数据库层面的分布式事务场景,而我们业务需求有时候不仅仅关乎数据库,也有可能是上传一张图片或者发送一条短信。
2.2 3pc: 三阶段提交
3PC 包含了三个阶段,分别是准备阶段、预提交阶段和提交阶段,对应的英文就是:CanCommit、PreCommit 和 DoCommit。
把 2PC 的提交阶段变成了预提交阶段和提交阶段,但是 3PC 的准备阶段协调者只是询问参与者的自身状况,比如你现在还好吗?负载重不重?这类的。
而预提交阶段就是和 2PC 的准备阶段一样,除了事务的提交该做的都做了
2.3 本地消息表
本地消息表其实就是利用了 各系统本地的事务来实现分布式事务
本地消息表顾名思义就是会有一张存放本地消息的表,一般都是放在数据库中,然后在执行业务的时候 将业务的执行和将消息放入消息表中的操作放在同一个事务中,这样就能保证消息放入本地表中业务肯定是执行成功的。
然后再去调用下一个操作,如果下一个操作调用成功了好说,消息表的消息状态可以直接改成已成功。
如果调用失败也没事,会有 后台任务定时去读取本地消息表,筛选出还未成功的消息再调用对应的服务,服务更新成功了再变更消息的状态。
2.4 最大努力通知
就本地消息表来说会有后台任务定时去查看未完成的消息,然后去调用对应的服务,当一个消息多次调用都失败的时候可以记录下然后引入人工,或者直接舍弃。这其实算是最大努力了。
事务消息也是一样,当半消息被commit了之后确实就是普通消息了,如果订阅者一直不消费或者消费不了则会一直重试,到最后进入死信队列。其实这也算最大努力。
所以最大努力通知其实只是表明了一种柔性事务的思想:我已经尽力我最大的努力想达成事务的最终一致了。
博主总结:
可以看出 2PC 和 3PC 是一种强一致性事务,不过还是有数据不一致,阻塞等风险,而且只能用在数据库层面。
而 TCC 是一种补偿性事务思想,适用的范围更广,在业务层面实现,因此对业务的侵入性较大,每一个操作都需要实现对应的三个方法。
本地消息、事务消息和最大努力通知其实都是最终一致性事务,因此适用于一些对时间不敏感的业务。