最新消息:

分布式事务系列探讨一:引子

未分类 niuge678 771浏览 0评论

完全没有使用数据库事务的公司

我今天第一次知道,现在的公司是完全没有使用MySQL事务的。

为什么不使用事务

1、数据库隔离级别不支持。我们现在使用的隔离级别是read-uncommitted, read-committed, repeatable-read, serializable。我们使用的是RC级别,不是RR级别。

2、分库分表导致分布式事务。

分库分表引发的问题

全局主键问题

分库分表后,一张表会跨越多张表,甚至是多个库,此时,数据库的自增 id 将变得没有意义。因此,我们必须要单独设计全局主键,以避免主键重复。

其实,这是一个比较成熟的问题,即有很多解决方案。下面说明几种常见的做法:

1、UUID:由一组32位数的16进制数字构成的唯一识别码,目的是让分布式系统中的所有元素都能有唯一的识别信息。它可以本地生成,性能很高;但是,由于它很长,会占用大量的存储空间。

2、额外的自增表:单独创建一张表,只有一个自增主键,而这个主键则用于分库分表的全局主键。这种做法需要依赖于其他表,且存在单点问题,当这张自增表出现故障,导致系统不可用。

3、分布式全局唯一 ID 生成算法:这种算法有很多,例如:Twitter 的 snowflake、美团的 Leaf 等等

由于前两种解决方案的问题较为明显,且不易解决。所以,业界在全局主键的生成问题上,都会使用 “分布式全局唯一 ID 生成算法”(有很多开源实现时,就不需要自己重复造轮子)。

关联查询问题

在分库分表之前,我们可以通过 JOIN 多表的方式来查询复杂的数据。但是切分之后,数据可能分布在不同的节点上,此时再去 JOIN 几乎是不可能的事情。所以,对于分库分表的情况,我们通常的建议是:“抛弃 JOIN”。

那么,为了解决关联查询的问题,我们可以想一些别的办法。例如:

1、字段冗余设计:这是一种反范式的设计,也是空间换时间的典例,它是将需要多次用到的数据分布到多张表中,避免了 JOIN 查询。

2、数据组装:也就是多次查询,将多次查询的数据组装在一起构成整体数据。

3、拆分查询:注意,这里所说的查询指的是前端发起的查询请求,即前端把复杂查询(多表)拆分成多次简单查询(单表)。

由此,可以得出结论:分库分表对于 “大” 业务系统几乎是不可避免的,但是,分库分表同样存在非常严重的缺陷。如果你不能解决这些问题或给出合理的解决方案,慎用分库分表,反而是可以考虑使用分布式数据库(例如 HBase)去代替。

事务一致性问题

分库之后,当业务更新(插入、更新、删除)的数据分布在不同的库中时,就会带来跨库事务问题。对于分布式事务来说,一般可以采用XA分布式事务协议(包含二阶段提交(2PC),三阶段提交(3PC)两种实现)。

虽然分布式事务解决方案最大程度的保证了数据库操作的原子性,但是在提交事务时需要协调多方,对提交事务造成了延迟,且会增高死锁的概率。所以,如果我们的业务系统不追求瞬时的强一致性,可以采用事务补偿的方式来达到最终的一致性。

解决方案

后面的系列添加。

阿里开源的分布式事务框架 Seata

2019年1月,阿里巴巴中间件团队发起了开源项目 Fescar(Fast & EaSy Commit AndRollback),其愿景是让分布式事务的使用像本地事务的使用一样,简单和高效,并逐步解决开发者们遇到的分布式事务方面的所有难题。后来更名为 Seata,意为:Simple Extensible Autonomous Transaction Architecture,是一套分布式事务解决方案。

Seata的设计目标是对业务无侵入,因此从业务无侵入的2PC方案着手,在传统2PC的基础上演进。它把一个分布式事务理解成一个包含了若干分支事务的全局事务。全局事务的职责是协调其下管辖的分支事务达成一致,要么一起成功提交,要么一起失败回滚。此外,通常分支事务本身就是一个关系数据库的本地事务。

Seata主要由三个重要组件组成:

TC:Transaction Coordinator 事务协调器,管理全局的分支事务的状态,用于全局性事务的提交和回滚。
TM:Transaction Manager 事务管理器,用于开启、提交或者回滚全局事务。
RM:Resource Manager 资源管理器,用于分支事务上的资源管理,向TC注册分支事务,上报分支事务的状态,接受TC的命令来提交或者回滚分支事务。

Seata的执行流程如下:

1、A服务的TM向TC申请开启一个全局事务,TC就会创建一个全局事务并返回一个唯一的XID
2、A服务的RM向TC注册分支事务,并及其纳入XID对应全局事务的管辖
3、A服务执行分支事务,向数据库做操作
4、A服务开始远程调用B服务,此时XID会在微服务的调用链上传播
5、B服务的RM向TC注册分支事务,并将其纳入XID对应的全局事务的管辖
6、B服务执行分支事务,向数据库做操作
7、全局事务调用链处理完毕,TM根据有无异常向TC发起全局事务的提交或者回滚
8、TC协调其管辖之下的所有分支事务, 决定是否回滚

Seata实现2PC与传统2PC的差别:

1、架构层次方面,传统2PC方案的 RM 实际上是在数据库层,RM本质上就是数据库自身,通过XA协议实现,而 Seata的RM是以jar包的形式作为中间件层部署在应用程序这一侧的。
2、两阶段提交方面,传统2PC无论第二阶段的决议是commit还是rollback,事务性资源的锁都要保持到Phase2完成才释放。而Seata的做法是在Phase1 就将本地事务提交,这样就可以省去Phase2持锁的时间,整体提高效率

引申:https://www.cnblogs.com/monkeyblog/p/10449363.html

转载请注明:牛哥678 » 分布式事务系列探讨一:引子

与本文相关的文章

发表我的评论
取消评论

表情

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址