1.结论:

Zookeeper实现了A可用性、P分区容错性、C中的写入强一致性,丧失的是C中的读取一致性,读取准确地说是顺序一致性
主要有以下三点:

  1. 从一个读写请求分析,保证了可用性(不用阻塞等待全部follwer同步完成),保证不了数据的一致性,所以是ap。
  2. 从zk架构分析,zk在leader选举期间,会暂停对外提供服务(为啥会暂停,因为zk依赖leader来保证数据一致性),所以丢失了可用性,保证了一致性。
  3. 进一步讲:这个c不是强一致性,而是最终一致性。即上面的写案例,数据最终会同步到一致,只是时间问题。

2.Zookeeper写入流程

  1. 首先client向zk Server发出一个写的请求,如果当前server不是Leader,那会把请求发送给Leader;
  2. Leader接收到以后开始发起Proposal到Follwer,不对Observer发送;
  3. Follower收到来自Leader的提议后,会返回ack响应;
  4. Leader收到ack请求后,会采用过半机制,即发送出去的提议有一半以上的ack响应,则就会发送commit提交数据,同时也会向Observer提交commit;
  5. 返回给客户端写入成功的回应;
  6. 若有新的服务器加入进来,也会对Leader进行数据同步,来达到集群中数据的一致性。

3.Zookeeper读取流程

  1. Client 向Zookeeper 发出读请求之后,无论请求的是Leader 还是 Follower , 都将会直接返回结果,如果使用sync读取,异步的实现当前进程与leader之间的指定path的数据同步,这样能读到最新的数据。

4.Zookeeper会脏读吗?

  会的,如果一个zk集群有10000台节点,当进行写入的时候,如果已经有6K个节点写入成功,zk就认为本次写请求成功。但是这时候如果一个客户端读取的刚好是另外4K个节点的数据,那么读取到的就是旧的过期数据。

  ZooKeeper并不保证在每个实例中,两个不同的客户端将具有相同的ZooKeeper数据的视图。由于诸如网络延迟的因素,一个客户端可以在另一客户端被通知该改变之前执行更新,考虑两个客户端A和B的场景。如果客户端A将znode / a的值从0设置为1,则告诉客户端B读取/ a,则客户端B可以读取旧值0,这取决于它连接到的服务器。如果客户端A和客户端B读取相同的值很重要,则客户端B应该在执行读取之前从ZooKeeper API方法调用sync()方法。

5.几种一致性解读

强一致性:又称线性一致性(linearizability )

  1. 任意时刻,所有节点中的数据是一样的,
  2. 一个集群需要对外部提供强一致性,所以只要集群内部某一台服务器的数据发生了改变,那么就需要等待集群内其他服务器的数据同步完成后,才能正常的对外提供服务
  3. 保证了强一致性,务必会损耗可用性

弱一致性:

  1. 系统中的某个数据被更新后,后续对该数据的读取操作可能得到更新后的值,也可能是更改前的值。
  2. 即使过了不一致时间窗口,后续的读取也不一定能保证一致。

最终一致性:

  1. 弱一致性的特殊形式,不保证在任意时刻任意节点上的同一份数据都是相同的,但是随着时间的迁移,不同节点上的同一份数据总是在向趋同的方向变化
  2. 存储系统保证在没有新的更新的条件下,最终所有的访问都是最后更新的值

顺序一致性:

  1. 任何一次读都能读到某个数据的最近一次写的数据。
  2. 系统的所有进程的顺序一致,而且是合理的。即不需要和全局时钟下的顺序一致,错的话一起错,对的话一起对(目前网上能查到的原话);前三种应该都好理解。强一致性就是在任意时刻,所有节点中的数据都是一样的。

  弱一致性就是可能访问的到更新后的值,也可能访问不到。

  最终一致性,不保证任何节点都是相同的,也就是说各节点的数据版本可能完全是混乱的,a节点是1,b节点是2,c节点是3,然后a节点更新到2,b节点更新到3,但能保证在没有更新后达成一致。

  Java内存模型中顺序一致性,如果对多线程并发有理解,可以结合下来理解顺序一致性内存模型是一个被计算机科学家理想化了的理论参考模型,它为程序员提供了极强的内存可见性保证。顺序一致性内存模型有两大特性:

  1. 一个线程中的所有操作必须按照程序的顺序来执行。
  2. (不管程序是否同步)所有线程都只能看到一个单一的操作执行顺序。在顺序一致性内存模型中,每个操作都必须原子执行且立刻对所有线程可见。

  这里的顺序一致性,讲的是一种多线程并发执行下理想情况,包含两种要求:

  1. 线程中的操作必须按照程序的顺序执行,也就是说,不能自己自作主张,更换执行顺序
  2. 线程中的操作是原子性的,执行了就是执行了,没执行就是没执行,不存在中间状态,而且一旦执行,其他变量应该立刻可见。

  联系到zookeeper,说点结论:

  1. 各节点的数据更新必须按照顺序进行。
  2. 数据写入执行情况,数据版本应对其他节点可见(leader能知道写入是否成功)。

  结合以上,你会发现,zookeeper并不是最终一致性,而是顺序一致性。

  1. 最终一致性的特点是,无法保证任意节点在同一时间某份数据是相同的,但是最终在没有新的更新时会达成一致。而Zookeeper所有节点的数据版本都是递增的,可能会有某个节点因故障版本低于大多数,但是是有序的,不会出现各自增长的情况。

  比如,Zookeeper节点可能会出现4台数据是version 5,一台数据是version4。但是不会是5台机器各自更新。

  所以这里对顺序一致性的定义是

  1. 任何一次读都能读到某个数据的最近一次写的数据。
  2. 对其他节点之前的修改是可见(已同步)且确定的,并且新的写入建立在已经达成同步的基础上。