ZooKeeper是典型被动复制的应用,而不是主动复制(复制状态机)。和主动复制一样,被动复制中每一个服务器都有一个一致性模块,一个状态机和一个日志模块。一旦客户请求到达主服务器,主服务器立即处理请求而不关心这个请求最终是否提交。然后(从原始状态开始)根据每一个请求驱动状态机,得到最终状态,最终复制到其它服务器的是这个最终状态而不是原始状态(请求)。这个日志提交后,本次提交对外部客户端可见。为了保证容错和线性扩展,主服务器将给客户端的回应记录在日志中,这样当主服务器切换到另外一个服务器的时候,依然可以回应给那些重试的客户端。
从一致性算法的角度看,被动复制和主动复制(复制状态机)是非常类似的。因此几乎所有的分布式一致性算法都可以很好地用于被动复制中。但是从总体上说,被动复制系统更加复杂,两者的差异主要体现在下列三个方面:

差异一
在被动复制中,状态机上执行的是尚未全局提交的日志,而在主动复制中执行的是已经完成全局提交的日志。这个差异决定被动复制中,主服务器收到客户端请求后立即驱动状态机并得到结果状态。同时状态机模块要保证只要日志尚未提交,日志对状态机的更改就对客户端不可见(这里类似传统RDBMS系统中事务隔离性的概念),如果发生切换导致另外一个服务器变成主服务器,旧主服务器需要回滚尚未提交的改变。(在MySQL中为了防止在Crash Recovery过程中提交内部XA事务,通常我们需要Truncate不该提交的事务的Binlog文件)。
差异二
主动复制的日志中包含所有的客户端请求,即便某些请求最终对状态机并不会带来改变。例如,一个带条件的写操作,由于条件不满足,这个请求不会对状态机带来改变,但是依然会存在于日志中并占用磁盘空间。当然这并不会对一个分布式系统带来容量方面的问题,因为日志最终都会通过某些方法进行归档。在被动复制中,如果一个请求对状态机没有产生任何改变,对于这个请求的最终状态和初始状态是相同的,主服务器并不会为这个请求产生操作日志(当然主服务器依然会等待到这个日志提交后再回应客户端)。
差异三
主动复制中的状态机是确定性的,因为所有的服务器在执行相同的请求序列后一定会达到一个相同的结果状态。例如,一个客户端请求的执行结果不应该依赖于服务器的当前时间。在被动复制中,主服务器的状态不需要是确定性的;它可以对请求做任何操作,只要状态机产生的最终结果是确定性的。幸运的是,大多数情况下可以使用一种混合方法来克服主动复制中的这个限制:服务器在收到客户端请求后可以附加一些不确定的输入,比如服务器的当前时间和随机数等等,并将这些不确定的输入和请求一并写入到日志中。这样,所有其它服务器都可以根据附加的输入来形成一个确定性的状态。
总结
主动复制中,一致性算法的核心作用是将可能并发的请求在各个服务器之间协调达成完全一致的执行顺序,操作的对象还是原始的客户端请求;而被动复制中,主服务器把原始的客户端请求作为本地状态机的输入,输出一个“结果”(对状态机的更改),一致性算法的核心作用是将这个“结果”在多个备份服务器之间达成一致的执行顺序。




