《NoSQL精粹》读书笔记,转载请注明出处《jiq?钦‘s technical Blog》
前面已经提到过,催生NoSQL的主要原因是:需要一种能够运行在大集群上的数据库。但是从关系型数据库迁移到面向集群的NoSQL数据库,最大的一个改变就是针对一致性的思考方式。关系型数据库通过“强一致性”避免各种问题,而NoSQL并非如此。
1
更新一致性
两个用户同时修改同一份数据,会发生“写冲突”。服务器肯定会以特定顺序处理这两个请求,写冲突即后者提交更新请求时所依据的数据都不是前者更新过的。
单服务器必定具备更新一致性,可以通过并发控制机制来保证一致性,有两种方式:“悲观”方式和“乐观”方式,前者是为了避免冲突,在写入之前先请求加锁,而后者允许冲突,最常见的就是条件更新,即在执行更新操作前先判断数据的当前值是否和上次读入的相同,是则成功更新否则更新失败。无论如何数据只能以一种更新顺序被处理,悲观方式和乐观方式都能够用于避免写冲突。
分布式环境处理写冲突:在分布式环境下:
1) 如果是“对等复制”的分布模型,同一份数据有多个备份,每个服务器处理更新请求的顺序可能不同,最终造成更新后保存的值不相同。这时可以采取一种叫做“写入仲裁”的方式来避免写冲突,即若发生两个相互冲突的写入操作,只有其中一个操作能为超过半数的节点所认可。
2) 如果是“主从复制”分布模型,采取的方法是:将某份数据的所有更新操作都交由一个节点执行,通过并发控制机制保证更新一致性。
2
读取一致性
某客户端在另一个客户端写入操作过程中读取数据,则会发生“读写冲突”。考虑一个包含商品项和运费的订单,A用户可以更新,B用户可以读取,A用户更新订单时会先更新了其中商品项,再更新运费,恰巧在两个写入操作之间B用户读出订单,就会导致数据不一致,这就是读写冲突现象。
单服务器必定具备读取一致性,比如关系型数据库通过“事务”这个概念,将两个写入操作封装到一个事务中,确保其它用户读出的数据要么是事务执行之前的值,要么是事务执行之后的值。
分布式环境处理读写冲突:大多数NoSQL数据库,尤其是面向聚合的数据库不支持事务,但是聚合数据单元支持“原子更新”,所以聚合内部可以保持读取一致性,可以将商品项和运费都放在一个订单聚合中。很显然我们不可能将所有数据放入一个聚合,当涉及多个聚合的更新操作时,无法保证读取一致性。
分布式环境下,不论是主从复制还是对等复制(从不同副本读取数据),会引发另一种新的不一致问题,假设某个酒店的在线订房,最后只剩下一间房间,分隔伦敦和洛杉矶两地的A和B两夫妻在电话讨论要不要订这间房,此时C在北京将这间房订下,但是这个更新的数据到达洛杉矶副本的时间比到达伦敦副本的时间早,这就导致A和B再次打开浏览器看到不一样的结果,这也是一种“读取一致性”,从不同的副本获取同一个数据项时,得到的值不同。此时若要保持强一致性:
1) 若是“对等复制”分布模型,可以采取一种叫做“读取仲裁”的方式;
2) 若是“主从复制”分布模型,只需从主节点读取数据就好了。
但是这个更新操作最终还是会传播到所有副本中,这就是所谓的“最终一致性”。
3
放宽一致性约束
要真正做到一致性,必须放弃系统中的其他一些特性,而这些特性偏偏可能是必不可少的,因此通常需要针对不同场景牺牲一定的一致性来保障其他特性。比如单服务器关系型数据库中通过事务保证较强的一致性,然而事务系统通常又具备放松“隔离级别”的功能,甚至有的关系型数据库为了追求性能可以完全放弃事务。
CAP理论:这是NoSQL领域中需要放宽一致性约束的原因。此定理的基本表述是:在一致性(Consistency)、可用性(Availability)、分区忍受性(Partition tolerance)三个属性中,最多只能同时满足其中两个。
u 一致性:即前面提到的更新一致性、读取一致性、复制一致性等。
u 可用性:分布式系统中某个无故障节点所接收的每一个请求,无论成功或是失败都必将得到响应。
u 分区忍受性:发生脑裂是集群仍然可用。脑裂即通信故障导致集群被分割成多个无法相互通信的网络分区。
CAP理论本质:一旦发生脑裂,集群就不可用,这个代价是非常巨大的,因此集群必须要满足“分区忍受性”,即发生脑裂时系统仍然可用。这就是CAP的精髓,CAP又可以表述为:“当分布式系统可能会遭遇脑裂时,我们需要在一致性和可用性之间做一个权衡”。
特别注意,这并不是一个二选一的过程,通常情况下我们都会略微舍弃“一致性”,以获取某种程度的“可用性”,这样产生的系统几部具备完美的可用性也不具备完美的一致性,两种不完美结合起来,却能够满足特定需求。
可用 = 延迟合理:回顾一下可用性的含义,其实与其考虑如何权衡“一致性”和“可用性”,如何考虑如何权衡“一致性”和“延迟”,因为无故障节点处理请求时,若超过了能忍受的最大延迟时间,我们就应该放弃操作,认为节点不可用。
放弃强一致性:从前面一致性介绍可以看到,要想保证较强的一致性,“主从分布”模型需要针对主节点进行读写,“对等复制”分布模型需要通过仲裁的方式,参与的节点越多,获得的一致性越强,可见这两种模型下要保证较强一致性都需要在“延迟”上做很大的牺牲。加之分布式环境下允许出现“更新不一致”或者“读取不一致”的场景很多。因此在分布式环境下,通常需要牺牲一定的一致性来获得较低的延迟。
此外、延迟还和持久性相关,可以舍弃一部分“持久性”来减小延迟,比如让数据库大部分时间都在内存中运行,更新操作也直接写入内存,并且定期将数据变更写回磁盘。