架构设计:分布式事务概述、服务以及库表拆分模式详解

一、分布式事务简介

1、转账经典案例

跨地区和机构的转账的业务在实际生活中非常常见,基础流程如下:

架构设计:分布式事务概述、服务以及库表拆分模式详解

账户01通过一系列服务和支付的流程,把钱转入账户02,在这一过程中,如果账户01出现出账成功,但是账户02没有入账,这就导致数据不一致,违反了基本的事务原则。基于数据归属在不同服务和不同的数据库中,这种情况下的事务出错被称为分布式事务问题。

2、基本概念

分布式事务是指事务的参与者、支持事务的服务器、资源服务器以及事务管理器分别位于不同的分布式系统的不同节点之上。

如上的转账案例,看似只有一次的转账操,实际上由不同的服务不同数据库的多个细节操作组成,这些无感知的细节操作分布在不同服务上,甚至属于不同的地区和应用,如何保证这些操作全部成功或者全部失败,即保证不同数据库间的数据一致性,这就是分布式事务需要解决的核心问题。

3、分布式事务特点

基于如下电商业务场景,基本分布式的架构思路:

架构设计:分布式事务概述、服务以及库表拆分模式详解

  • 数据库基于业务特点,进行分库分表;
  • 数据库拆分,随之就是业务的服务化(SOA);

基于电商业务进行拆分,会出现常见的:订单,用户,库存,物流等一系列的服务,管理不同的业务数据库,在实际的下单支付应用场景下,需要同时操作用户,订单,库存等多个服务,就必须保证数据一致性,下单支付成功,库存必须就需要用到分布式事务。

二、CAP基础理论

1、基础简介

说到分布式事务问题,必然会说下CAP理论,分布式系统的三大指标:

架构设计:分布式事务概述、服务以及库表拆分模式详解

Consistency:一致性

单个事务执行更新写操作,操作结束成功返回,在同一时间的其他事务读取的数据完全一致,不存在中间状态。在分布式的系统中描述:用户下单支付,扣款,减库存,生成物流,必须一致。例如限量打折促销中,用户下单后库存没减少,这就导致不一致问题。

Availability:可用性

服务必须一直处于可用的状态,收到用户的请求,服务器必须在有限的时间给出回应,不管结果是处理成功或者处理失败。

Partition tolerance:分区容错

通俗说,在分布式系统中,一个流程里可能出现某个服务出错情况,这是无法绝对避免的,在程序设计上要能容忍这种错误发生。

2、CP和AP模式

分布式系统很难同时满足一致性、可用性、分区容错性三个特点,在大部分的系统架构中,都会选择CP或者AP模式,即需要抛弃一个特点,说明一点,为何P没有抛弃,对于分布式系统而言,分区容错是该架构模式下的基本原则,不同的SOA服务和数据库是比如会被部署到不同的节点下。所以如何解决C(一致性)和A(可用性)就成分布式系统的最大痛点。

为何不能同时满足C和A,这也是基于分布式架构特点看,不同服务直接不能保证通信是100%成功,一旦出现失败情况,一致性和可用性就无法满足。

既然强一致性无法保证,那退一步,给处理时间,最后结果保证一致性,也可以,这就涉及到BASE理论。

三、BASE基础理论

1、基础简介

BASE理论是由eBay公司的架构师提出的,主要是对上述的CAP理论中一致性和可用性做的权衡结果,基于CAP定律逐步演化而来,核心思想;即使无法做到强一致性,但每个应用都可以根据自身业务特点,采用适当策略实现数据的最终一致性。

Basically Available:基本可用

分布式系统在发生故障的时,允许损失部分可用性。例如常见电商清仓甩卖时,为保证主业务可以,一些不重要的服务直接降级提示。

Soft State:软状态

允许系统中的数据存在中间状态,并认为该中间状态的存在不会影响系统的整体可用性。相对于原子性而言,要求多个节点的数据副本都是一致的,这是一种硬状态。

Eventual Consistency:最终一致

强调的数据更新操作,即软状态必须有个时间期限,在经过一段时间的同步之后,最终都能够达到一个一致的状态。因此,最终一致性的本质是需要系统保证最终数据能够达到一致,而不需要实时保证系统数据的强一致性。时间期限长短取决于延时、负载、数据同步等各种因素。

BASE理论提出是基于大规模高可用可扩展的分布式系统架构,不同于关系型数据库事务特点(ACID)的强一致性模型,通过牺牲强一致性来获取更高的可用性,并允许数据在一段时间内是不一致的,但最终达到一致状态。实际的业务场景下事物(ACID)基本特性和BASE理论也是要权衡考虑。

2、柔性事务

遵循BASE理论,利用业务特点,在指定期限内让事务保持最终一致性,柔性事务是一种思想,从根本上看,就是业务模式对于事务过程中不一致性有一定的容忍度,可以留出足够的时间执行事务最终一致的方法。

3、PAXOS算法

Paxos算法一种保障分布式系统最终一致性的共识算法,利用的是选举策略,少数服从多数的思想。PAXOS不要求对所有节点做实时同步,实质上是考虑到了分区情况下的可用性,通过减少完成一次事务需要的参与者个数,来保障系统的可用性。

例如:N个服务节点,有(N/2)+1个节点达成共识,则认为系统达到了一致,并且按照Paxos原则,最终理论上也达到了一致,不会再改变,如此一来,只要保证有半数以上的服务存活,允许小部分服务挂掉,客户可以与大部分服务节点通信,那么就不会影响整体操作流程,也不需确保服务器全部处于工作状态,容错性非常好。操作影响的数据和结果随后会被异步的同步到其他节点上,从而保证最终一致性。

四、服务间隔离

1、分布式结构

分布式系统架构的明显特点,就是按照业务系统的功能,拆分成各种服务,每个服务下面都有自己独立的数据库,以此降低业务间的耦合度,隔离不同的数据库保证系统最大的稳定性等。

架构设计:分布式事务概述、服务以及库表拆分模式详解

例如上图是电商系统中经典的业务场景,订单-仓储-物流的服务模式,不同服务提供不同的应用场景,服务间存在通信机制,以此实现服务的高可用。

2、隔离思想

分布式的架构体系中,涉及一个根本思想逻辑:隔离;

服务和数据库根据业务拆分,进而隔离开来,整个架构中某个服务挂掉,不会影响其他的服务继续执行。例如上述1中:如果物流服务挂掉,影响的是用户无法实时追踪物流状态,但是不会影响订单的持续产生。

隔离的策略也是各有不同,常见的电商系统是典型的按照业务特点进行拆分,这种就是不同的业务场景下,使用不同的服务和数据库;还有一种业务场景,多租户平台,针对大客户提供独立的服务和数据库,对小客户提供公服务和数据库,这种策略比较现实:大客户带来收益多,完全覆盖服务和数据库的成本,必须保证不能被一些非必要因素影响。

不管是基于什么策略拆分隔离,首先都必须面对数据库设计的问题。

五、数据库设计

1、拆分思想

数据库在业务体系不大的情况,一般都是单库出现,最多加一个备份库以备不时之需,当业务体量不断扩大,就会考虑拆分场景,例如常见的:水平拆分,垂直拆分策略。

水平拆分

首先把单表表分割N个结构相同的表,然后把数据按照策略分散到不同的表中,这是表层面;如果把表在分散在不同的数据库中,这就是数据库层面的水平拆分。

垂直拆分

把单表中数据按照不同特点,拆分成两张不同的表,常见的策略是根据数据是修改多,还是读取多,把修改频繁的字段放一张表,读取频繁的放另一张表,这是表层面;如果根据业务特点,拆分不同库,这就是数据库层面。

2、拆分模式

读写分离

读写分离是数据库拆分的最基本方式,实现起来难度也不大,只需要根据读写库的配置,把业务中数据写操作路由到写库,数据读操作路由到读库即可。

架构设计:分布式事务概述、服务以及库表拆分模式详解

这种方式实现的数据库拆分虽然相对容易,如果出现主从复制挂掉的情况,就会导致数据读不到,或者数据读取延时,所以在强一致的要求的情况下,使用不多。

分库分表

分库分表主要用来解决单表数据量过大的问题,根据特定字段的路由规则,把数据分散到不同的库,不同的表中。

架构设计:分布式事务概述、服务以及库表拆分模式详解

通常是基于一些唯一值的哈希算法实现的分库分表策略。也有一些成熟的中间件可以集成到项目直接使用,这种模式更多适用于单点数据的查询的场景,可以基于路由快速定位数据所在的库表。

业务分库

基于业务特点拆分数据库,是当前分布式架构下,或者微服务模式的基础用法,不同业务场景下数据放在一个库,因为数据关联性很强,在使用的时候方便,同时与其他业务数据隔离开来,避免单点故障导致数据库挂掉。

架构设计:分布式事务概述、服务以及库表拆分模式详解

这种模式虽然看起来更合理,但是复杂度也是非常的陡,因为两种业务场景下的数据不可能绝对没有关联,比如订单库一定依赖用户库的信息,这就需要订单服务和用户服务之间需要通信,引发的问题就会很多。

用户分库

在多租户场景下,会根据客户流水大小提供不相同的服务和数据库,这是一个十分现实的策略,毕竟可能一个大客户的月流水超过几个小客户的总和。

架构设计:分布式事务概述、服务以及库表拆分模式详解

既然可以根据客户情况分库,也可以基于其他策略,比如地区,常见云服务的应用,选择华南,华北,华东区之类的。

六、架构体系难点

这里所提到的涉及问题,是指基于业务分库模式下的出现的问题。

1、服务依赖

在分布式架构体系下,不同服务都有各自的数据库,但是数据之间一定是有关系的,服务A要用服务C的数据库,就必须通过服务C提供的接口来获取,这是基本机制,不然拆分服务和库就没意义了,这样就会导致服务间产生依赖关系。

架构设计:分布式事务概述、服务以及库表拆分模式详解

如上图,如果订单服务和论坛服务同时依赖用户服务,那么就要考虑如果用户服务挂掉,会影响多大的范围,做好权衡,还有一个关键点,如果多个服务依赖一个服务,那么就要保证被依赖的服务有足够的能力应对,例如这里,如果订单服务有10W的流量,论坛服务有10W的流量,那么就要保证部署上用户服务起码要能承受20W的流量。

2、分布式事务

既然数据库在不同的服务下面,服务之间又存在依赖关系,那么保证数据的事务一致性就是非常大的难题。

这里基于支付业务的转账场景做一个简单的演示,从数据源1的账户表中,向数据源2的账户表中操作转账,尽管在代码层面看添加了事务最高级别的控制,但是却没有起到控制作用,导致出账成功,但是入账失败,这就是典型的分布式事务问题。

@Service
public class AccountServiceImpl implements AccountService {
    @Resource
    private JdbcTemplate jdbcTemplateOne ;
    @Resource
    private JdbcTemplate jdbcTemplateTwo ;
    /**
     * @param fromUser 出账 账户
     * @param toUser   入账 账户
     * @param money    涉及 金额
     */
    @Transactional(isolation= Isolation.SERIALIZABLE)
    @Override
    public void transfer(String fromUser, String toUser, int money) {
        // fromUser 出账
        jdbcTemplateOne.update(
                "UPDATE user_account SET money = money-? WHERE username= ?",
                    new Object[] {money, fromUser});
                    
        int i = 1/0 ;
        
        // toUser 入账
        jdbcTemplateTwo.update(
                "UPDATE user_account SET money = money+? WHERE username= ?",
                    new Object[] {money, toUser});
    }
}

总结

以上就是小编整理的分布式概述,如果感觉比较晦涩,没关系,小编之前也有整理过架构宝典,需要深入了解学习的朋友,点击即可阅览,希望能够帮到大家更好的学习!!!

喜欢请多多点赞评论转发,关注小编,你们的支持就是小编最大的动力!!!

猜你喜欢

转载自blog.csdn.net/python6_quanzhan/article/details/108550584