zoukankan      html  css  js  c++  java
  • replication 技术摘要

     
    先定义两种一致性:
    1.读写一致性
      从读写这个角度来看,数据库的基本目标就是最小化副本之间不一致的时间并且保证最终一致性。
      最终一致性是一种弱的一致性,那么还有两种更强的一致性:
         1. read-after-write 一致性:一个数据项X被写成功后,后续的读操作总是能读到最新的数据。
         2. read-after-read  一致性:如果一个数据项X被读到了,那么后续的读操作读到的X的值会和之前读到的一样或者只会比这个值更新,不会更老。
     
    2. 写写一致性(一共描述三种解决写写冲突的模型,其中第一种模型不能很好的解决read-modity-write问题)
        应用程序通常跟写相关的其实就是两种场景:
             一种就是简单的set a=1,另外一种就是set a=a+1
             第一种不需要读a的值,即independent write,  另外一种需要读a的值,就是read-mofidy-write
             下面的第一种一致性模型可以解决set a=1的场景,但是不能解决a=a+1的场景
        一、Atomic Writes
              数据库提供一种API,原子的写某个值,即写这个值之前不需要读这个值,即文中所说的independent atomic assignment of a value
             这种方法解决写写冲突的方法,我的理解是(不一定准确):
             假设对于数据项A,有2个replica,分别用a1和a2标识,
             对每个replica,都维护多版本,每个版本可以用这个replica接受到写请求的时间来标识,即用timestamp来标识。前提:
             在数据库内部,需要保持多个replica所在的机器时钟同步(当然可以有微小误差)。假设有两个客户端C1,C2
             C1 将数据项A设置为1,C2 将数据项A设置为2,假设C1的写请求路由到了replica  a1所在的机器,C2的写请求路由到了replica a2
            所在的机器,那么由于a1和a2所在的机器之间有网络,这个网络有延迟或者可能有network partition,导致说,C1首先将a1 设置成了1,然后将
            这次写传播给a2,同理,C2首先将a2设置成了2, 然后将这次写传播给a1,导致的后果就是,对同一个数据项A的两次更新请求
            到A的两个replica 的顺序不一致,这将会最终导致replica 的不一致。即,a1=2, a2=1.
            解决这个问题的方法就是:为a1和a2 都维护多版本,即replica  a1 有多个版本,replica a2也有多个版本,版本以timestamp标识。
            假设C1的这次写到达a1的时间是7点零1秒,C2到达a2的时间是7点零2秒,那么replica  a1将产生一个7点零1秒的版本,replica a2
            将产生一个7点零2秒的版本,然后C1的写请求被传播到a2,这次传播带着7点零1秒这个时间戳过来,这样的话,就在replica a2上产生了一个
            7点零1秒的版本,同理,在replica a1上产生了一个7点零2秒的副本。那么当应用程序来读的时候,指定timestamp来读,或者不指定的话,就是最    新的,那么系统就会把7点零2秒的这个版本的值给应用程序。
              
          二、Atomic Read-Modify-Write:
            应用场景:两个客户端同时读取item A,然后将A+1,然后写入A,我们理想的结果是A最后等于A+2,但是在atomic write的场景下,得到的值是A+1
            (atomic write 提供的API是直接设置一个值的API)。
               针对这种场景:数据库可以提供两种解决方案来解决:
               1. Conflict prevention
                         比如使用2PC这种分布式锁或者使用paxos 这种一致性协议来解决,这种情况下,就相当于将
                         read-modify-write 当作一个事务来处理。还有一种一个data item  有一个主replica,专门用于写
                   2. Conflict detection
                          数据库在运行时跟踪是否有冲突,如果检测到了冲突,就是回滚某次更新,或者两个版本都保留,留给客户端去解决冲突。
                         一般并发更新的检测是通过vector clock检测出来的,riak,voldemort,couchdb就是使用的这种方法。
     
           
    replication方法有很多种,不同的技术在 consistency-scalability-availability-latency 之间进行tradeoff
     
    下面从一致性的角度从弱到强开始介绍:

    A:anti-entropy:假设有3个副本。

      写:写任何一个副本
      读:读任何一个副本
       replication策略,后台通过anti-entropy协议进行更新的传播
        这种方法很烂,即时没有节点挂,那么也不能很好的处理写写冲突和读写一致性的问题。不详述。
     
    B:基于上一种方法的改进的方法就是,写操作异步的写所有的在线副本,只要有一个成功了就代表写成功了,如果当时有一个节点不在线了,那么这个节点重新上线后,它的更新是通过anti-entropy 来进行传递的。
    C:在上一种方法中,对于下线的节点重新上线后的更新使用hinted handoff技术来进行处理更好。给下线节点的更新记录在coordinator 上
     
    read one write one:读一个节点,然后触发一个异步读,向请求的数据所在的所有的replica 去请求一个hash value,来解决不一致性。不是很懂。

     
     
     
    ReadQuorum-WriteQuorum: 保证R+W > N,能保证每次读一定能读到最新的数据,我们可以给数据带上timestamp信息,就可以知道谁是最新数据。

     这种方法,当write操作执行成功后,读操作再来的时候是能够保持强一致性的。但是当write操作还没有执行完的时候,读就来了话,那么可能读的数据
    时新时旧。如下图所示:

    发生这种情况的原因在于对2个 replica的写操作不是原子性的。
     
    ReadAll-WriteQuorum:

     
    上面这些技术要么提供的是atomic write级别的一致性,要么提供的是Read-modify-write with Conflict Detection 级别的一致性。
    为了达到 Conflict Prevention 级别的一致性,不得不使用中心化或者lock的机制。一个简单的例子就是类似于mysql
     master-slave 方式,写都写master,然后异步复制到slave
     
    Transactional Read One Write All:write的过程使用2PC,但是2PC在coordinator fail的时候不太靠谱,
    会导致资源挂起,所以可以使用性能更差的paxos

     
  • 相关阅读:
    [LeetCode] 75. 颜色分类(荷兰国旗)
    [LeetCode] 347. 前K个高频元素
    CMU-14445 数据库原理 汇总
    MIT-6.824 操作系统 汇总
    发布一个基于协程和事件循环的c++网络库
    记录一次gdb debug经历
    彻底弄懂UTF-8、Unicode、宽字符、locale
    CPU使用率原理及计算方式
    TCP使用注意事项总结
    STL-vector
  • 原文地址:https://www.cnblogs.com/foxmailed/p/2708591.html
Copyright © 2011-2022 走看看