美文网首页
《Designing Data-Intensive Applic

《Designing Data-Intensive Applic

作者: 赤子心_d709 | 来源:发表于2018-02-28 13:16 被阅读62次

    1.说明

    事务,是一个把若干读写放入同一个逻辑单元的方式。
    概念上,事务的读写要么全成功(commit),要么全失败(abort,rollback)。即使失败,程序也能安全的重试。
    有了事务,处理错误会变得很简单,因为不用担心部分错误的问题。
    有了事务,db也会有安全保障,应用程序可以忽视特定的错误场景以及并发的问题。

    实际上,不是所有应用都需要事务,那么怎么知道自己需不需要事务,这就要理解事务能够提供的安全保证以及对应的代价。下面看一系列问题,如并发控制,多种竞争的触发以及db实现的隔离级别

    2.概念

    如今几乎所有的关系db以及部分非关系db都支持事务
    部分非关系db默认提供分区,备份的支持,而这种场景下,事务则成了受害者。这些新生代的db放弃了食物或者提供了非常弱的保证。

    如今关于分布式db以及事务的关系,有两种观点

    第一种观点是事务制约了拓展性,任何大型系统应该抛弃事务来保证高可用性以及效率。
    另一种观点认为,db必须提供事务的保证,完成对“有价值数据”的某些处理。
    两种观点都夸张了。
    

    实际上,所有技术选型都有优势以及制约,稍后会深入细节讲解事务的trade-off.

    2.1 ACID

    事务提供的安全保证,常称为ACID,即原子性,隔离性,一致性,持久性。这是1983年Andreas Reuter和 Theo Härder提出的。
    然而,不同db对他们的实现各不一样,甚至对于I(隔离性)都有不同的理解。
    不幸的是,ACID几乎成了一个市场用语。

    Atomicity

    广义来说,原子性表示一个东西不能够拆解的更细了。
    在事务中,原子性表示:

    所有事务的所有操作要么都成功,要么都失败。
    不会因为某些原因(断电,网络中断)等原因造成数据处于部分成功,部分失败的状态。
    这使得失败的事务可以进行重试,而不会使得数据出现duplicate或者错误。

    Consistency

    一致性这个词到处都在用

    备份一致性(如最终一致性)
    一致性hash
    CAP理论(后续第9章再介绍)
    

    然而在事务中,一致性表示:

    在应用的层面上,数据处于一个正确的状态
    ACID中,AID其实都是db的特性。然而C(一致性),是一个应用程序的性质
    应用程序需要db的原子性和隔离性,来达到应用程序的一致性

    比如一个会计系统,数据要保证收支平衡。这是程序自己保证的。
    如果程序自己写入收支不平衡的错误数据,db并不能阻止。

    Isolation

    多个client同时访问db的同一份数据时,会出现并发竞争问题。
    如下图实例,两个client获取当前值并进行+1操作


    图1

    由于出现了竞争,数据本该从42变成44,结果变成了43

    隔离,表示同时执行的事务之间互不干扰。
    db保证并发执行的事务的结果要和他们串行执行的结果一样.

    实现中,串行化的隔离基本不用,因为代价很高。有些db如Oracle中会用快照隔离,是比串行化隔离弱一点的隔离级别,后面会讲

    Durability

    持久性是一个承诺,一旦事务成功提交,它所写的任何数据将不会丢失,即使有硬件故障或数据库崩溃。
    在单节点数据库中,持久性通常意味着数据已写入非易失性存储(如硬盘驱动器或SSD)。它通常还需要写入日志,以便出现文件损坏时恢复工作。
    在分布式数据库中,持久性可能意味着数据已成功复制到一些节点上。

    当然,完美的持久性是不存在的,比如所有备份,硬盘同时被损坏。

    2.2 单obj以及多obj的操作

    ACID中的AI描述了一个事务中包含多个写时,db应该处理的事情

    原子:提供要么全成功要么全失败的保证,不会出现中间状态
    隔离:并发执行的事务互不影响,一个事务要么看到另一个事务的所有写,要么看不到任何写

    上述定义假定多个事务在同时修改多个obj(记录)

    下图以收发邮件为例,查找收件人未读的邮件,以及从邮箱中查找未读邮件的个数

    图2,user2看到了user1的未提交的写
    上图违背了事务的隔离性,是不满足ACID要求的。称之为脏读
    多对象的事务请求 要求有一种方式决定哪些读写属于同一个请求,这可以通过client的tcp链接知道
    在BEGIN TRANSACTION以及COMMIT之间的语句都被认为是同一个事务
    

    很多非关系型db对于原子性支持不好(即使有multi operation)
    会存在部分数据更新成功但是部分数据失败的情况

    单obj的写

    原子性能够用崩溃恢复日志实现
    隔离性能用对象锁实现
    

    有些db提供复杂的原子操作,利用自增操作解决图1中的read-modify-write的问题
    类似的是CAS操作,即compare and set,这里不展开

    这些操作十分有用,能够阻止多个client同时写一个obj时出现的lost updates问题,后面会介绍

    对于 多obj的写 的需求

    现状:
    很多分布式存储放弃了多obj的事务,因为在分区中太难实现了,另外还会影响性能。

    是否需要多obj的写:
    很多场景单object操作就够了,但是也有场景要处理多object,即外键,二级索引等等。
    这些场景当然也可以不用事务实现,但是缺乏了原子性和隔离性会有下述后果
    缺乏原子性,错误处理变得更复杂。
    缺乏隔离性,会出现并发问题。下一节会介绍Weak Isolation levels。

    2.3 处理错误以及丢弃错误

    事务的特性决定了 它不会出现执行了一半的情况。

    现状:
    不是所有系统遵从这个规矩,比如在无leader模型中,会尽量执行,如果遇到了错误他们也不会回滚,由应用程序自行从错误中恢复。
    错误最终会发生,但是很多开发缺盲目乐观,忽视了对于错误的错综复杂的处理。

    如何处理:
    事务出错之后,重试是一种简单有效的方法,但是并不完美

    1.执行成功但是网络原因导致重试,会执行两次
    2.如果负载压力过大,重试会加重负载
    3.短暂的错误之后重试才有用(如死锁,网络中断等),永久的错误是不值得重试的
    4.如果事务对db以外的部分有影响,如发送邮件,这个影响可能会持续,即使事务已经被丢弃了
    5.如果client重试失败了,所有写的数据都会丢失
    

    名词总结

    主要涉及名词如下,在本章内容都会有详细介绍

    ACID

    快照隔离
    脏读

    read-modify-write
    lost updates
    Weak Isolation levels

    思考

    无leader模式是不支持事务的:
    因为无法回滚,保证不了原子性

    图1的例子中,两个事务之间怎么就互相干扰了呢?
    可以理解为,第一个事务开始执行却还没有commit时,第二个事务也处于了同样的状态。
    出现了race condition

    错误的解决方式是重试,重试的代价如何
    文章上面有写

    总结

    本章介绍了事务以及ACID的定义
    对于单obj以及多obj的操作,现状进行了一定的展开
    对于错误处理以及重试的代价进行了分析

    暂时不知道的问题

    是否有分布式事务
    事务和分区,备份是怎样的结合,能全部结合还是部分结合
    zk的multi op与事务的关系,以及支持

    refer

    https://www.jianshu.com/p/a84e4f41a2aa

    相关文章

      网友评论

          本文标题:《Designing Data-Intensive Applic

          本文链接:https://www.haomeiwen.com/subject/yuwmxftx.html