沈洵:分布式事务原理与实践之多机事务

来源:互联网 发布:linux lcd驱动程序 编辑:程序博客网 时间:2024/05/16 01:40

目标?

完整的事务支持,像传统单机事务一样的操作方式,可按需无限扩展。

事务最重要的一件事就是易用本身。但,容易理解的模型往往性能都不好,性能好的模型往往不容易理解。

什么是事务?

让很多步骤顺序发生,多进程/线程看上去 就像是一步操作。
public class DrdsPrefTest {private String share = "DRDS是阿里分布式数据库";private ReentrantLock lock = new ReentrantLock();/** * share是共享变量 * 共享变量意味着多个不同的线程可以访问这个变量 * @param args, 非共享变量,java的方法栈动态生成的区域 * 多线程环境下,让计算机像我们一样去理解这个系统。 * 简单抽象来看,一个输入参,一个共享数据 ,进行一段计算,返回一个结果,最后返回一个共享数据的版本 * 再把它抽象来看,很多时候对数据库的操作就是操作这个共享数据。只是将原来本地的共享数据变成了一个远程的共享数据而已。 * 就是存储和计算分离的操作,存储和计算分离后会有性能的代价。 */public String doSth(String args){lock.lock();try {String tempStr = share + args;this.share = tempStr;return tempStr;}finally{lock.unlock();}}}

事务的本质?

多线程共享同一个内存数据的一个处理方式,java所有处理内存的方式都是可以直接映射的数据库的。而java里的处理内存的并发的方法,和数据库里内存处理并发的方式是一样的。
唯一的差异,是一个是远程的一个是本地的。而远程就意味着更多的命令的交互存在,但是核心的算法是完全相同的。
最终的整个效果就是多线程的程序在整个代码片段开始lock,unlock中间的流程写成什么样子,最终计算机运行就是什么样子,而且会通过各种各样的方式来保证尽可能的高效。
事务的本质:共享数据的情况下,如何能够尽快在最高并发度提供读写的能力。

分布式系统上的获得与失去?

无限的扩展性,无限的可用性。
无限的可用性:
7*24不断,一直持续服务。
最难的是一致性
cap 事务原理 
代价是花更多的钱,在更多的节点部署机器,然后承担延时的影响
无限的扩展性:
保证一致性和低延迟是分布式里最难的。
原来一台机器的数据共享非常容易,指针指向另一个内存块时间是纳秒级的。两台机器上做这件事就是从一边复制到一边然后两边数据才能共享。
A有这个数据而B机器上没有这个数据,A的数据是不能被访问的,协议进一步复杂了,会有多次交互,网络的latency是毫秒级的。导致系统能力上不去。

一致性(容易理解)和可用性?

CAP A可用性的关键,单位的延迟下能完成请求,而且保证请求的一致性。
扩展性和易用性里很多时候会选扩展性

数据共享两种模式?

消息传递,容易扩展,代价很高。
基于数据本身的内存共享,没法扩展,代价很低。


网络带来的,网络失去的?

基于锁的事务实现中遇到问题

从2PL到2PC
分布式事务异常处理
分布式日志记录
分布式事务延迟变大的问题

结合MVCC的事务实现中遇到的问题

分布式顺序问题

从2Phase Lock到2Phase Commit
问题:
Commit(unlock Bob, unlock Smith)
如果A和B在不同机器,应该如何提交?
加锁、去锁操作的分布式系统化
一旦出现在不同机器上,commit会出问题。commit需要在两台机器上。

2phase commit是可以完全保证强一致性事务的。
mvcc可能会出现乱序的情况。
单机事务的优化往往是从排它锁,再到读写锁,再到MVCC,这些系统的优化都是针对不同场景的优化。

回顾一下

读读优化,读写锁
写读,读写优化:MVCC
写写操作是不能优化的,但是也有两种可能性:乐观锁和悲观锁。

从排它锁的方案来了解他,但是在排它锁之上多了一个步骤,一个commit变为两个。
如果两个commit一个成功了,另一个不成功怎么办?
commit bob成功了,commit smith失败了。
针对这种情况有几种处理方式
第一种,是努力送达,bob成功,一定保证simth成功。
第二种,bob成功了,smith没成功就停在那儿等待,这样有个好处保证了可用性。
第三种情况不存在:
没法回滚,只要commit了bob,就没办法回滚,因为bob的锁已经解开了,数据对其他人可见了。就可能出现状态不一致的场景。
针对两段提交协议的核心就在于这次commit怎么办,针对这个操作只有两种处理方式,一种是努力送达(大部分),另一种是人工介入。
现在大部分系统主要核心处理思路都是努力送达模型。而传统意义上的数据库的处理方式都是人工介入。
一个数据库不可用的时候,比如宕机了被洪水冲走了,彻底挂了没法恢复。但是如果下面还有一层,比如分布式系统上smith的账户也有仨,可用性就提高了,宕掉任何一台机器还是可用的。


逻辑不可用和系统不可用要分开。

第一件事,系统不可用,机器宕机了,内存条坏了,网络断了,系统不可用可以自动化处理。数据不一致往往都是系统不可用。
逻辑上的错误,smith钱不够,bob账号被删除了,用应层的commit和rollback就可以解决。
逻辑上的问题开始的时候已经加过锁了,只要没有commit可以做任何操作。
谁来处理这次不可用?
那就是协调器,如何保证一个commit失败,系统出现问题的时候可以不断的重试。
commit一次和commit100次是一样的,要保证操作的幂等性。幂等的做法用trasactionID来保证。

协调者必须是高可用的,协调者必须是多机

任意协调者必须能够知道这个事务运行的状态
    记录日志
        Prepared阶段只需记录一次日志
        每个节点的Commit都必须记录日志
问题:
    日志如何记录才能保证不丢?
    日志如何记录才能保证高可用?
    

网络交互的延迟是毫秒级的

看起来简单的单机事务在在分布式系统上需要更多的网络操作。
分布式事务,锁的维持时间增加了,并行度就会下降。违背了锁的范围尽可能小,锁的维持时间尽可能少的优化原则。



MVCC分布式系统上的问题

时间戳
    SCN(Oracle)
    Trx...id(Innodb)
    Etc...
读写排序
    分布式的困局
    逻辑时间戳
        一台机器去分配号码,这台机器就是单点
        一组机器分配号码

如果是一台机器来分配之间就会知道happen before的关系。
MCVCC处理写读冲突和读写的冲突,用版本号来标记。
一种用逻辑时间,每次操作原子号自增1。另一种方式是物理时间。
单机情况虾一般是用逻辑时间,mvcc一定可以做出来。分布式系统上时间戳分布在分布式节点上完成,难度大,代价高。

分布式事务的主要难点?

    事务延迟变大的问题
    事务异常处理
    日志记录
    MVCC顺序问题
  

分布式事务改善方案分析?

确保兼容单机数据库
尽最大努力保证扩展性
尽最大努力保证高可用和数据安全

共享磁盘方案

    锁延迟变大的问题
    采用远程内存直接访问RDMA(FC/IB)
    Oracle RAC
    事务的异常处理
    放弃分布式事务
    Oracle RAC
    

 其他问题?

真实的开发种还需要范式吗?
范式是尽可能在少冗余数据的情况下,最合理的设计表结构。范式可以提供一种参考方法。
多对多的场景会有个中间表。所有的查询需要join这张表,在分布式系统上这张表可能在不同的机器上。

总之,
计算机是一个分形系统,从简单到复杂不断迭代,每个点做了微妙的变化,有时候可以复用原来东西,有时候不能复用原来的东西,如果不知道原来的东西,就不知道未来在哪里。







0 0