在OpenStack中,数据库是主要系统“状态”的主要来源。大部分Core Projects都使用传统关系型数据库作为系统数据和状态的存储,另外如Ceilometer使用了MongoDB,还有其他Incubator Projects使用了Redis作为队列或者状态存储。数据库给OpenStack提供了状态组件并把状态的“共享”问题交给了数据库,因此解决OpenStack的扩展问题实际上就是解决使用的数据库本身的扩展问题。比如OpenStack HA Solution最令人头疼的就是传统关系数据库或者其他数据存储的扩展问题,数据库扩展问题的根源是其本身不支持分布式和良好的扩展性,而这个根源又会衍生出分布式系统最大的噩梦–“网络分区”。

下面会分析”网络分区“给数据库扩展带来的问题,同时在OpenStack组件中如何规避和解决。

分布式系统的基本: 数据一致性

现代软件系统由一系列“组件”通过异步、不可靠的网络相互沟通构建。理解一个可信赖的分布式系统需要对网络本身的分析,而“状态”共享就是一个最重要的问题。

举一个例子,当你发表一篇博文后,你可能想知道在你点击“发布”操作之后:
1. 从现在开始会对所有人可见
2. 从现在开始会对你的连接可见,其他人会延迟
3. 你也可能暂时不可见,但是未来会可见
4. 现在可见或者不可见:发生错误
5. …… etc

不同的分布式系统会有对一致性和持久性相互影响的权衡和决定,比如Dynamo类系统通过NRW来指定一致性,如果N=3, W=2,R=1,你将会得到:
1. 可能不会马上看到更新
2. 更新数据会在一个节点失败后存活

如果你像Zookeeper来写入,那会得到一个强一致性保证:写操作会对所有人可见,比如这个写操作在一半以下的节点失败后仍然能够保证。如果你像MySQL写入,取决于你的事物一致性级别,你的写操作一致性会对所有人、你可见或者最终一致性。

网络分区

分布式通常假设网络是异步的,意味着网络可能会导致任意的重复、丢失、延迟或者乱序的节点间消息传递。在实际中,TCP状态机会保证节点间消息传递的不丢失、不重复、时序。但是,在Socket级别上,节点接发消息会阻塞,超时等等。

检测到网络失败是困难,因为我们唯一能跟得到其他节点状态的信息就是通过网络来得到,延迟跟网络失败也无从区分。这里就会产生一个基本的网络分区问题:高延迟可以考虑作为失败。当分区产生后,我们没有渠道去了解到其他节点到底发生了什么事: 它们是否还存活?或者已经crash?是否有收到消息?是否正在尝试回应。当网络最终恢复后,我们需要重新建立连接然后尝试解决在不一致状态时的不一致。

很多系统在解决分区时会进入一个特殊的降级操作模式。CAP理论也告诉我们要么得到一致性要么高可用性,但是很少有数据库系统能够达到CAP理论的极限,多数只是丢失数据。

接下来的内容会介绍一些分布式系统是如何在网络失败后进行相关行为。

实例1:传统数据库与2PC

传统的SQL数据库如MySQL、Postgresql都提供一系列不同的一致性级别,然后通常都只能向一个primary写入,我们可以把这些数据库认为是CP系统(CAP理论),如果分区发生,整个系统会不可用(因为ACID)。

那么传统数据库是不是真的是强一致性?它们都是使用2PC([1]策略来提交请求:
1. 客户端commit
2. 服务器端写操作然后回应
3. 客户端收到回应完成提交

在这三个步骤中,可能发生不一致的情况在于2与3之间,当服务器写操作完成但是回应没有被客户端收到,无论是超时或者网络故障,客户端这时会认为这次操作没有完成,而事实上数据库已经写入。这时就会产生不一致的行为。也就是客户端得到的错误并不能解释到底服务器端有没有写入。

2PC不仅在传统SQL数据库被广泛使用,也有大量用户实现2PC在MongoDB之上来完成多键值事务操作。

那么如何解决这个问题?首先必须接受这个问题,因为网络失败地概率比较低,并且正好在服务器写操作完成与客户端得到回应之间失败。这使得受到影响的操作非常稀有,在大部分业务中,这个失败是可接受到。相对的,如果你必须要强一致性的实施,那么应该在业务中付诸行动,比如所有的事务写操作都是幂等的,是可重入的。这样当遇到网络问题时,retry即可而不管到底写操作有没有完成。最后,一些数据库可以得到事务ID,通过track事务ID你可以在网络故障后重新评估事务是否完成,通过数据库在网络恢复后检查其记录的事物ID然后回滚相应事务。

我们在OpenStack的选择就很有限,目前各个项目中并不是所有写操作都是幂等的,不过幸运的是,OpenStack的数据在罕见的2PC协议特例中损失是能接受的。

实例2: Redis

Redis通常被视为一个共享的heap,因为它容易理解的一致性模型,很多用户把Redis作为消息队列、锁服务或者主要数据库。Redis在一个server上运行实例视为CP系统(CAP理论),因此一致性是它的主要目的。

Redis集群通常是主备,primary node负责写入和读取,而slave node只是用来备份。当primary node失败时,slave node有机会被提升为primary node。但是因为primary node和slave node之间是异步传输,因此slave node被提升为primary node后会导致0~N秒的数据丢失。此时Redis的一致性已经被打破,Redis这个模式的集群不是一个CP系统!

Redis有一个官方组件叫Sentinel(参考[Redis Sentinel],它是通过类似Quorum的方式来连接Sentinel instance,然后检测Redis集群的状态,对故障的primary节点试用slave节点替换。Redis官方号称这个是HA solution,通过Redis Sentinel来构建一个CP系统。

考虑Redis Sentinel在网络分区时候的情况,这时Redis集群被网络分成两部分,Redis Sentinel在的大区域可能会提升Slave node作为primary node。如果这时候一直client在连接原来的primary node,这时会出现两个primary node(split-brain problem,脑裂问题)!也就是说,Redis Sentinel并没有阻止client连接Old primary node。在此时,已经连接到old primary node的client会写入old primary node,新的client会写入到new primary node。此时,CP系统已经完全瘫痪。虽然Redis集群一直是保持运行的,但是因为依赖于Quorum来提升slave节点,因此它也不会是AP系统。

如果使用Redis作为Lock service,那么这个问题会成为致命问题。这会导致分区后同时可以有两个client获取同一个锁并成功,lock service必须是严格的CP系统,像Zookeeper。

如果使用Redis作为queue,那么你需要接受一个item可能会被分发零次、一次或者两次等等,大部分的分布式队列都保证最多只分发一次或者最少分发一次,CP系统会提确切一次的分发然后带来较高的延迟。你需要明确使用Redis作为队列服务的话必须要接受网络分区后队列服务可能导致的不稳定。

如果使用Redis作为database,那么可想而知,利用Redis Sentinel建立的database是不能称为database的。

最后,以目前的Redis来说,使用官方提供的组件它只能成为Cache。构建一个分布式的Redis前往[WheatRedis]

实例3: MongoDB

MongoDB采用类似于Redis的集群方式,primary node作为单点写操作服务然后异步写入replication nodes。但是MongoDB内建了primary选举和复制状态机,这使得primary node失败后,整个MongoDB会进行交流然后选择一个合适的slave node。然后MongoDB支持指定primary node可以确认slave node已经把写操作写入log或者真正写入,也就是通过一定的性能损耗来换取更强的一致性当primary node失败后。

那么MongoDB是否可以认定为是一个严格的CP系统?还是与Redis类似的问题,在网络分区后,当primary node在小的分区里,大的分区里的node会选举产生一个新的primary node,而此时在分区的时候,这两个node是会同时存在的(这个没有问题),然后当分区恢复后,小分区里的old primarynode会把在脑裂期间的操作发送到new primary node,这时候可能会产生冲突!

那么如何面对这个问题?接受它,首先这个冲突的概念像2PC一样可以在client端解决,同时MongoDB目前有WriteConnern可以解决这个问题,但会造成巨大的性能影响。

实例4: Dynamo

Dynamo是在传统的primary-slave模式遇到问题时候出现的红宝书,借鉴Dynamo的产品在一段时间内出现的非常多。

之前提到的系统都是面向CP的,起码是面向CP设计的。Amazon设计的Dynamo鲜明地面向AP。在Dynamo,它是天然地分区友好型,每一个node都是平等的,通过NWR来指定不同地一致性级别和可用性。这里不会详细阐述Dynaomo的原理([Dynamo](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf),每一个试图了解分布式系统的人都应该对Dynamo这篇论文非常熟悉,即使它面临很多问题,但是论文中出现的对Dynamo设计的思考和变迁是宝贵的。

那么当分区发生时,Dynamo发生了什么?首先根据NWR的推荐设定(W+R>N),小区是不能得到新的写操作,新的对象会写在大区。然后在分区恢复后,小区的对象会滞后并与新的对象发生冲突。这里的冲突解决策略非常多,如Cassandra使用的client timestamps,Riak的Vector clock,如果无法解决,冲突可能会硬性覆盖或者推到业务代码。

然后Dynamo本身没有任何方法来判断一个节点是否数据同步,也无法判断,只能通过完全的数据比较,而这个过程是代价昂贵并且不灵活的。因此Dynamo提到说(W+R>N)可以达到强一致性是不可能的,故障节点只会是最终一致性。

因此,解决Dynamo的问题像前面一样,接受它。首先你的数据可以设计成immutable,然后你的数据决定可以在罕见情况下丢弃或者变旧,再或者使用CRDTs来设计你的数据结构。无论如何,Dynamo始终是一个good idea并且它推动了分布式设计的发展。

实例5: BigTable

上面提到的系统都是面向分布式的,要么AP要么CP。那么Bigtable是AC系统,虽然我们介绍的一直是分区问题,但是我们也需要考虑在中心化设计的Bigtable。无论是HBase还是HDFS都是这类设计,它们回避了分区问题并且在单IDC下达到非常好的效果。这里不会详细讨论中心化设计,因为它根本就没有考虑分区问题。

思考: 分布式数据库系统的设计

通过上述的分析可以了解到构建一个分布式数据库集群的困难,无论是同步复制,异步复制,Quorum还是其他的,在网络分区面前,任何挣扎都是无力的,网络错误意味着”I don’t know” not “I failed”。

构建一个“正确的”分布式数据库系统通常在几个方面达成意见:
1. 接受罕见的问题
2. 使用开源的软件,分布式系统会产生极大的“漩涡”在“理论正确的分布式算法”和“实际使用的系统“。一个有Bug的系统但是正确的算法比一个错误的设计更能接受。
3. 利用问题进行正确的设计,如使用[CRDTs](http://pagesperso-systeme.lip6.fr/Marc.Shapiro/papers/RR-6956.pdf)
4. split-brain问题是分区的原罪,如何解决split-brain之后的遗产才是正确的解决方案

小结

如何在OpenStack上做到HA是OpenStack官方和其他发行版公司都在努力的方向,而其中关键就在于数据存储的HA和一致性,在这个方向上,我们通过对”网络分区“这一关键问题的分析并在不同类型的数据库上进行落地思考,可以得到如何在其上规避、解决和接受它。通过在OpenStack的产品上思考这些问题,我们可以在HA Solution上有更强健的基础。

下面一篇会介绍在OpenStack项目的上下文中,各个不同数据库的应用会带来怎样的问题和后果。

文章来源 : http://www.ustack.com/blog/distributedsystems_networksdivision/

分布式系统与网络分区相关推荐

  1. 分布式系统中的分区问题

    文章目录 分布式系统中的分区 分区与复制 键值数据的分区 根据键的范围分区 根据键的散列分区 分区与次级索引 基于文档的二级索引进行分区 基于关键词的二级索引进行分区 分区再平衡 反面教材:hash ...

  2. rabbitmq 网络分区错误

    介绍: 系统 centos6.5 应用 rabbitmq集群(2台) 版本 rabbitmq3.3.5 rabbitmq.config 是默认配置 {vm_memory_high_watermark, ...

  3. 模拟RabbitMQ网络分区

    欢迎支持笔者新作:<深入理解Kafka:核心设计与实践原理>和<RabbitMQ实战指南>,同时欢迎关注笔者的微信公众号:朱小厮的博客. 欢迎跳转到本文的原文链接:https: ...

  4. raft协议对网络分区的处理

    网络分区是raft协议需要解决的异常问题之一,假设有A,B,C,D,E五个节点.假设A和E网络不通,但是他们和B,C,D都是通的如下图: 假设某一时刻leader是A节点,E在超时没有收到心跳后,把t ...

  5. RabbitMQ学习笔记:集群和网络分区(Network Partitions)

    集群成员之间的网络连接故障会影响客户机操作的数据一致性和可用性(如CAP定理).由于不同的应用程序对一致性有不同的要求,并且对不可用性的容忍程度不同,所以可以使用不同的的分区处理策略. 1.检测网络分 ...

  6. 个体功能网络分区的分割方法

    识别个体大脑的独特功能结构的是走向个体化医学和理解人类认知和行为变化的神经基础的关键一步(主要是因为当下的功能分区模板都是population水平的,虽然所有人的大脑在结构上和功能上是具有一定的人群一 ...

  7. android windows 共享文件,Android上的Windows共享-文件,文件夹和网络分区

    在Android上访问Windows共享 使用Android上的Windows Share的本教程是什么? 当我说Android上的Windows共享时,是指从您的Android手机或平板电脑访问Wi ...

  8. 分布式系统(微服务架构)的一致性和幂等性问题相关概念解析

    前言 什么是分布式系统?关于这点其实并没有明确且统一的定义.在我看来,只要一个系统满足以下几点就可以称之为分布式系统 系统由物理上不同分布的多个机器节点组成 系统的多个节点通过网络进行通信,协调彼此之 ...

  9. 拜托!这才是分布式系统CAP的正确打开方式!

    "纸面"上的CAP 相信很多同学都听过CAP这个理论,为了避免我们认知不同,我们先来统一下知识起点. CAP理论在1999年一经提出就成为了分布式系统领域的顶级教义.并表明分布式服 ...

最新文章

  1. 一个Python小白5个小时爬虫经历
  2. A - Til the Cows Come Home POJ - 2387
  3. 他用五年研究百位百万富翁生活习惯 结果很震撼
  4. 时间序列相关算法与分析步骤
  5. 开源的数据库,PostgreSQL 基础入门实战
  6. 可视化管理_供应链可视化管理的应用与展望
  7. 【吴恩达机器学习】学习笔记——1.5无监督学习
  8. comment.html手机文件,comment.html
  9. 导入新工程,提示“Migrate Project to Gradle?”
  10. openai-gpt_OpenAI的GPT-3:货物崇拜编程人员的终结
  11. 安卓编程用什么软件_手机上能安装PLC编程软件吗?为什么?
  12. 几个可以查看Centos系统版本命令的方法
  13. 24 Hour Wallpaper for Mac(动态桌面壁纸软件)
  14. 计算机基础(01)基础知识
  15. python:网络数据收集
  16. 微信隐藏的功能和技巧
  17. JQuery之Ajax方法
  18. %20ld c语言,C语言第二次实验报告 - osc_ldea7g3t的个人空间 - OSCHINA - 中文开源技术交流社区...
  19. python的函数式编程实例_函数式编程例子
  20. windows7与linux共享文件夹oracle,ORACLE expdp备份到windows网络共享文件目录(NFS)

热门文章

  1. linux 下 `dirname $0`
  2. iText7解套(二)中文行首行末标点符号处理
  3. Hadoop,master和slave简单的分布式搭建
  4. ERNIE-ViL: Knowledge Enhanced Vision-Language Representations Through Scene Graph
  5. 【冰糖R语言】Pearson、Spearman相关性及其显著性 cor() rcorr()
  6. GraphQL 学习笔记
  7. 万得-python接口-获取数据
  8. 南方电网计算机招聘笔试,南方电网招聘笔试题(附答案).PDF
  9. 使用nginx实现请求转发的功能
  10. 虚拟机报错模块“Disk”启动失败。 未能启动虚拟机。