五 NoSQL一致性解决方案
更新一致性,读取一致性,放宽一致性约束,放宽持久性约束,CAP定理,仲裁
先来一个案例
Tom和jarry在同一家公司上班,有一天,他们发现公司网页显示的电话有误,于是进入后台系统同时修改电话。我们假定他们输入的电话号码格式稍有差别,那么,他们提交的新号码就会略有不同,此时将产生“写冲突”问题:两人在同一时刻更新同一条数据。
1 更新一致性。
在并发环境下维护数据一致性是一件麻烦事。通常,我们将其分为“悲观方式”和“乐
观方式”。
悲观方式:避免在更新时发生冲突,采取“写入锁”,这样,在某个值修改之前,必
须先获得“写入锁”,系统确保某一时刻只有一个客户能够获得这把锁。如果tom和jarry想同时获得者“写入锁”,那么只有Jarry(若服务器按照首字母顺序执行序列)能获取该锁,而tom看了jarry修改后的数据,再来决定是否继续更新它。
乐观方式:在任意客户执行操作前,都要先测试数据的当前值和其上一次读入的值
是否相同。在这种情况下,Jarry的更新能够成功执行,而tom的将操作失败。Tom得知失败后,他可以再次查询该数据,以决定是否要继续修改。
当然,上面所说的这两种模式都有一个先决条件,那就是更新操作的顺序必须一致。在单服务器环境中,这显然成立,它必须完成一个操作,才能处理下一个。然而,当服务器数量不止一个(对等复制),那么两个节点就可能按照不同的次序更新操作,这样照成的结果是,每个节点最终保存的电话号码不一致。
所以,乐观方式2:将两份更新数据都保存起来,并标出它们存在冲突。更svn或者git相同,在处理冲突时,该方式遵循与版本控制系统相同的步骤。必须以某种方式将两个冲突的更新操作“合并”提交。
2 读取一致性
在传统的关系型数据库中,我们常常采用事务来保证数据库操作的唯一性。
有种常见的说法是”NoSQL“数据库不支持事务。事实上,对不同的NoSQL来说,其对事务支持能力并不一样。例如图数据库一样支持ACID事务
面向聚合的数据库通常支持”原子跟新“,但仅限于单一聚合内部。
结合之前所说的”主从式分布模型“和”对等式分布模型“在任意时刻中,节点中都可能存在”复制不一致“,然而只要不再继续执行其他更新操作,那么上一次更新操作的结果最终将会分布到全部节点中去。过期的数据通常成为”陈旧“数据。
对于读取一致性的问题,我们通常用”会话一致性“来解决。会话一致性通常有两种实现方式,其一为:”黏性会话“,也就是绑定到某个节点的会话,只要某个节点能具备照原样读出所写内容的一致性,那么与之绑定的会话就都能具备这种性能。它的缺点是降低”负载均衡“性能。另一种会话一致性的方式为,”版本戳“。
3 放宽一致性约束
一致性是个好东西,在涉及系统时我们总是在避免出现不一致的现象。然而,要真正做到这一点,通常需要放弃系统中的其他一些特性,然而那些特性却是必不可少的。
权衡利弊,用“容忍度“来对应各个领域的不一致性。
CPA定理:给定一致性(Consistency),可用性(Availability),分区耐受性(Partition tolerance)这三个属性,我们只能同时满足其中两个属性。
可用性:如果客户可以同集群中的某个节点通行,那么该节点就必然能够处理读取及写入操作。
分区耐受性(脑裂):如果发生通信故障,导致整个集群被分割成多个无法互相通信的分区时,集群仍然可用。
单服务器系统显然是种”CA系统“,也就是说,具备一致性与可用性,但却不具备分区耐受性。
我们之所以要探讨“CAP定理”,是因为在权衡分布式数据库的“一致性”时,经常会提到并且“滥用它”,然而,与其考虑如何权衡“一致性”和“可用性”,不如思考怎么样在一致性和延迟之间取舍。在讨论分布式系统的“一致性”问题时,通常可以概括的说:参与交互操作的节点越多,“一致性”就越好。然而问题是,每新增一个节点,都会使交互操作的响应时间变长。“可用性”可以视为能够忍受的最大延迟时间,一旦延迟过高,我们就会放弃操作。并且认为数据不可用,这样一来,就和”CAP定理”对“可用性”所下的定义相当吻合了。
四仲裁
一致性与持久性之间的取舍,并不是一个非此即彼的议题。处理请求所涉及的节点越多,避免不一致问题的能力就越强。这自然引出一个问题,要想保持强一致性,需要使用多少个节点才行。
假设某份数据需要复制到三个节点中,为了保持强一致性,不需要所有节点确认写入操作,只需要其中两个节点(也就是超过半数的节点)确认就可以了。在这种情况下,如果发生两个相互冲突的写入操作,那么只有其中一个操作能为超过半数的节点所认可,这就是“写入仲裁”,如果用稍微正规一些的方式说那就是W>N/2。这个不等式的意思是,参与写入操作的节点数,必须超过副本节点数的一半。副本个数又称为“复制因子”。
与写入仲裁相似,还有读取仲裁。也就是说想要保证能够读取到最新的数据,必须与多少个节点联系才行?读取仲裁要复杂一些,因为它取决于确认写入操作所需的节点数。
现在考虑“复制因子”为3的情况。假设写入操作需要两个节点来确认(W=2),那么我们至少得联系两个节点,才能保证获取到最新的数据。然而,加入某些写入操作只被一个节点所确认(W=1)那么,我们必须和三个节点都通信一遍,才能确保取到最新数据。在这种情况下,由于写入操作没有获得足够的节点支持率,说以可能会产生更新冲突。但是,只要从足够数量的节点中读出数据,就一定能侦测出此类冲突。因此,即便在写入操作不具备强一致性的情况下,也可以实现出具有强一致性的读取操作来。
执行读取操作是所需联系的节点数(R),确认与写入操作时所需征询的节点数(W)以及复制因子(N)这三者之间的关系,可以用一个不等式来表述。那就是,只有当R+W>N是,才能保证读取操作的强一致性。
上述两个不等式都适用于“对等式分布模型”。如果使用“主从式分布模型”,那么只需向主节点中写入数据,就可以避免“写入冲突”了。