分布式事务与分布式锁

2020-02-28  本文已影响0人  没有昵称啊2333

一、分布式事务

什么事分布式事务

分布式事务就是指事务的资源分别位于不同的分布式系统的不同节点之上的事务。

分布式事务产生的原因

1. 数据库分库分表

在单库单表场景下,当业务数据量达到单库单表的极限时,就需要考虑分库分表,将之前的单库单表拆分成多库多表。

分库分表之后,原来在单个数据库上的事务操作,可能就变成跨多个数据库的操作,此时就需要使用分布式事务。

在这种场景下,事务的提交会变得相对复杂,因为多个节点(库)的存在,可能存在部分节点提交失败的情况,即事务的ACID特性需要在各个不同的数据库实例中保证。比如更新db1库的A表时,必须同步更新db2库的B表,两个更新形成一个事务,要么都成功,要么都失败。

2. 业务服务化

业务服务化即业务按照面向服务(SOA)的架构拆分整个网站系统。

比如互联网金融网站SOA拆分,分离出交易系统、账务系统、清算系统等,交易系统负责交易管理和记录交易明细,账务系统负责维护用户余额,所有的业务操作都以服务的方式对外发布。

一笔金融交易操作需要同时记录交易明细和完成用户余额的转账,此时需要分别调用交易系统的交易明细服务和账务系统的用户余额服务,这种跨应用、跨服务的操作需要使用分布式事务才能保证金融数据的一致性。

3. 解决方案

3.1 两阶段提交(2PC)

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

(一)准备阶段

协调者询问参与者事务是否执行成功,参与者发回事务执行结果。

(二)提交阶段

如果事务在每个参与者上都执行成功,事务协调者发送通知让参与者提交事务;否则,协调者发送通知让参与者回滚事务。

需要注意的是,在准备阶段,参与者执行了事务,但是还未提交。只有在提交阶段接收到协调者发来的通知后,才进行提交或者回滚。

存在的问题:

3.2 2PC应用之XA

XA需要两阶段提交: prepare 和 commit.

因为XA 事务是基于两阶段提交协议的,所以需要有一个事务协调者(transaction manager)来保证所有的事务参与者都完成了准备工作(第一阶段)。如果事务协调者(transaction manager)收到所有参与者都准备好的消息,就会通知所有的事务都可以提交了(第二阶段)。MySQL 在这个XA事务中扮演的是参与者的角色,而不是事务协调者(transaction manager)。

XA 事务的缺点是性能不好,且无法满足高并发场景。一个数据库的事务和多个数据库间的XA 事务性能会相差很多。因此,要尽量避免XA 事务,如可以将数据写入本地,用高性能的消息系统分发数据,或使用数据库复制等技术。只有在其他办法都无法实现业务需求,且性能不是瓶颈时才使用XA。

3.3 2PC应用之TCC

TCC 其实就是采用的补偿机制,其核心思想是:针对每个操作,都要注册一个与其对应的确认和补偿(撤销)操作。它分为三个阶段:

特点:不与具体的服务框架耦合,位于业务服务层,而不是资源层,可以灵活的选择业务资源的锁定粒度。TCC里对每个服务资源操作的是本地事务,数据被锁住的时间短,可扩展性好,可以说是为独立部署的SOA服务而设计的。强隔离性,严格一致性要求的业务活动。适用于执行时间较短的业务,比如处理账户或者收费等等。

TCC事务的优缺点:

二、分布式锁

为什么使用分布式锁

在单机场景下,可以使用 Java 提供的内置锁来实现进程同步。但是在分布式场景下,需要同步的进程可能位于不同的节点上,那么就需要使用分布式锁。

分布式锁应该具备哪些条件

在分布式系统环境下,一个方法在同一时间只能被一个机器的一个线程执行
高可用的获取锁与释放锁
高性能的获取锁与释放锁
具备可重入特性(可理解为重新进入,由多于一个任务并发使用,而不必担心数据错误)
具备锁失效机制,防止死锁
具备非阻塞锁特性,即没有获取到锁将直接返回获取锁失败

分布式锁实现

1. 数据库的唯一索引

当想要获得锁时,就向表中插入一条记录,释放锁时就删除这条记录。唯一索引可以保证该记录只被插入一次,那么就可以用这个记录是否存在来判断是否存于锁定状态。

存在以下几个问题:

2. 基于 REDIS 的 SETNX 实现分布式锁

setnx 只有在 key 不存在的情况下,才能 set 成功,可以通过 key 判断是否处于锁定状态。

3. Redis 的 RedLock 算法

为了取到锁,客户端应该执行以下操作:

优点:使用 Redlock 算法,可以保证在挂掉最多 2 个节点的时候,分布式锁服务仍然能工作,这相比之前的数据库锁和缓存锁大大提高了可用性,由于 redis 的高效性能,分布式缓存锁性能并不比数据库锁差。

缺点:失效时间设置多长时间为好?如何设置的失效时间太短,方法没等执行完,锁就自动释放了,那么就会产生并发问题。如果设置的时间太长,其他获取锁的线程就可能要平白的多等一段时间。

4. Zookeeper

利用 Zookeeper 的顺序临时节点,来实现分布式锁和等待队列。

缺点:所有取锁失败的进程都监听父节点,很容易发生羊群效应,即当释放锁后所有等待进程一起来创建节点,并发量很大。

参考:

上一篇下一篇

猜你喜欢

热点阅读