简单聊聊分布式必须面对的问题

760 阅读7分钟

在聊分布式项目前,我们需要知道一个概念,分布式的意义在于从容应对高并发和单点故障,所以分布式项目一般会和微服务一起使用。简而言之,分布式就是解决单点故障,分散服务器压力;而微服务就是分散功能,将复杂的项目根据业务进行抽取,独立成一个单独的服务,不会由于单个服务的宕机,影响到其他功能。

分布式项目中需要考虑的问题

分布式事务

1.1 事务

1.1.1 事务的概念

事务可以看做是一组操作,要么都成功,要么都要失败。举个简单的例子,张三向王五转账1000元,那么需要保证张三的账户执行-1000,王五的账户+1000,两个操作都必须成功。

1.1.2 事务的特性

原子性 :原子是最小单位,事务的原子性决定了事务不可以被分隔。Mysql中,是通过innodb引擎的undolog日志保证原子性。undolog日志是记录旧数据的,当其中一个操作出现问题,会按照日志记录将数据进行回滚。

隔离性 :两个事务之间应该相互隔离,互不影响。

一致性 :事务执行后,需要满足一些我们预定的规则。例如账户余额不能小于0,在转账情况下总额不变等。

持久性 :事务执行后,可以将数据持久化同步到磁盘,永不丢失。Mysql中,是通过innodb的redolog日志实现,记录新数据,当操作进行完成都没有出现问题,那么会将日志中的数据同步到数据库,完成持久化。

1.1.3 事务的传播机制

1.1.4 mysql中脏读、不可重复读、幻读问题

脏读 :在读取数据是,读取到了其他事务未提交的数据。

不可重复读 : 在同一事务中以相同的条件进行读取数据,数据不一致的情况,针对于其他事务在做修改操作。

幻读 : 在同一事务中以相同的条件进行读取数据,数据不一致的情况,针对于其他事务在做新增或者删除操作。

1.1.5 mysql的事务隔离级

读未提交 : 会出现脏读、幻读、不可重复读问题

读已提交 : 会出现不可重复读、幻读问题

可重复读 : 可以解决以上三个问题,其中幻读是通过间隙锁完成

串行化 : 可以解决以上三个问题

1.2 分布式事务

1.2.1 分布式事务概念

一个请求,涉及到多个数据源或者跨进程的操作,所涉及到的事务就是分布式事务。

1.2.2 分布式微服务项目中,如何减少分布式事务

分布式事务其实一个很复杂很棘手的问题,所以在设计微服务框架时,尽量将相同业务划分到同一个服务中,避免服务之间的调用,这样既可以节约内存,也可以减少处理分布式事务。

1.3 cap理论和Base理论

cap理论 :

c : 一致性。关注数据的一致性,站在用户的角度,肯定是希望强一致,但是在实际开发中,我们会根据业务的性质,具体考虑是强一致还是最终一致。

a : 可用性。在合理的时间内,将数据返回给用户。这里合理的时间,是根据项目实际情况而定,在多少时间内处理完成可以接受的意思。

p :分区容错性。在分布式微服务项目中,必然存在服务调用,就会涉及到网络问题,那么网络调用延迟等问题,就是这里的分区容错。

很多小伙伴认为,在分布式微服务中,cap理论只能三选二,其实并不然,基于分布式微服务的情况下,分区容错必须得到保证,否则就不是一个分布式项目了,所有只能是ca二选一。

1.4 分布式事务处理方案

1.4.1 2pc两段式提交

2pc两段式提交分为准备阶段和提交阶段 准备阶段 : 事务参与者会向TM(事务管理器)之间会进行通讯,各个事务都准备完成,才会进入提交阶段。

提交阶段 : 事务管理器会通知事务参与者,提交自己的事务,如果在准备阶段有事务参与者返回false,则发出回滚的指令。

问题:

在第一阶段,如果参与者迟迟没有回复状态,会造成阻塞。

在第二阶段,如果出现宕机情况,无法判断那些事务参与者收到了消息。

1.4.2 tcc

选择ap实现。保证弱一致性。

提供三个接口,tyr(预留资源),confirm(扣减资源),cancle(回滚)。

问题 : 实现复杂,需要和业务代码冗余,不方便后期维护。

1.4.3 最大努力通知

一般情况下,只有边缘非核心业务会选择,例如:积分成功这些通知类,尽最大努力通知n次,还是无法同步,就放弃。

1.4.4 可靠消息最终一致性

方案一 :本地消息表。将事务做持久化,定时任务扫描信息表,服务队列监听消息即可。

方案二 :rocketmq 。

1.生产者发送一个half消息到一个特定 队列 查看队列是否正常 并返回结果给生产者

2.此时生产者接收到half消息成功发送的结果 提交本地事务

3.将本地事务提交结果发送给消费者监听的队列,如果是成功的给消费者消费 如果是失败的 回滚

4.发送消息的时候如果没有返回结果 此时需要生产者提供一个可以查询本地事务提交结果的接口 供队列查询

1.4.5 seata方案

1.向tc事务协调器注册一个全局事务 生一个全局事务的XID

2.开始执行每个分支事务,但是此时携带了全局事务的xid,每一个分支事务都需要根据这个XID向TC注册分支事务

3.执行每个分支的业务代码,并且写入undolog,然后提交本地事务,并将本地事务的提交状态告知TC

4.全局事务管理器TM,向TC发送全局事务提交的请求,此时从TC获取所有分支事务的状态

5.如果所有分支事务状态提交都是成功的,删除分支事务的undolog即可

6.如果有一个分支事务失败,每个事务分支执行undolog进行回滚

分布式锁

2.1 lock锁和Synchronized关键字

1.synchronized是java中的关键字,我们无需去关注锁的释放,不会出现死锁(jdk会自己处理),而Lock是一个接口,是我们自己创建的,可能会出现死锁,需要在finally中释放锁;

2.Lock可以让等待的线程中断处理,Synchronized不行,我们无法控制;

3.synchronized是非公平锁,Lock可以手动设置(默认是非公平);

4.Lock的实现类ReentrantReadWriteLock,可以获取读锁和写锁;

5.Lock只能在用在代码块,Synchronized可以锁代码块、对象实例和类;

6.Lock可以分组唤醒线程,Synchronized要么唤醒一个线程,要么全部唤醒。

2.2 分布式锁的实现

关于后面的知识,下次再聊。。现在有点困了。

线程

3.1 线程和进程

3.2 线程创建的方式

3.3 线程生命周期

3.4 线程阻塞

经典秒杀案例

4.1 秒杀面临的问题

4.2 秒杀流程

4.3 秒杀细节