Percolator与分布式事务思考(三)

Percolator的事务实现依赖一个全局的时间戳服务来生成严格递增的时间戳,因为每个事务需要连接时间戳服务2次,这个服务必需能够很好的扩展. 这个预报服务通过写一个最高可分配的时间戳到持久存储中周期性的分派一个范围的时间戳段;给一个已分配时间戳段,那么预报服务能够在内存中严格按增量给请求分配时间戳(译者:现在看来,这个时间戳服务是单机的。).如果预报服务重启,时间戳将会跳到最大的已分配时间戳(我们不会让时间戳往后退).为了减少RPC请求(在事务延迟时间不断增长下)每个Percolator worker在跨事务的场景下通过维持一个预先准备的预报服务的RPC请求批量提交时间戳请求.随着预报服务压力越来越大,批量请求能够自然地弥补这种压力.批量请求时间戳让预报服务的扩展性得到了很好的提升,并且并不影响预报服务对分配的时间戳的保证(严格递增).我们的预报服务单台机器每秒提供大约2百万个的时间戳.

事务协议使用严格递增的时间戳来保证Get()操作返回该事务开始时间之前所有的已提交的写.让我们来看看它是如何提供这种保证的, 假如一个事务R在Tr时间戳进行读并且事务W 在Tw(<Tr)时间戳提交了(写);我们将会知道R会看到W的写.因为Tw<Tr,我们知道时间戳预报服务肯定先给出了Tw或者和Tr在同一批时间戳中;因此,W请求Tw先与R收到Tr.我们知道R不可能在收到它的开始时间戳Tr之前进行读操作并且W在请求他的提交时间戳Tw之前写入了锁.因此,上述特性保证W必需在R进行任何读操作前至少写入了所有的锁;R的Get()将会看到完整已提交的写记录或者是锁,也就是说W将会阻塞直到锁释放.无论哪种方式,W的写对R的Get()操作是可见的.

Percolator有一个通知机制,作用是最原始的数据发生变化后自动通知用户代码执行后续的操作,包括事务,所有用户写的观察者代码会被打成一个二进制包,然后让其在系统中的每一个tablet服务器上单独运行.每一个观察者注册了一个方法和几个column在Percolator中,Percolator会在任何行的这些列(任意一个)写入数据后触发这些方法.

Percolator的应用被组织成了一系列观察者;每个观察者完成一个任务并且通过向表写数据的方式创建更多的工作给接下去的观察者.在我们的索引系统中,一个MapReduce任务通过运行装载器的事务(loader transactions)装载爬取过来的文档到Percolator中,而这个行为触发了后续对文档进行索引的一系列文档处理事务(解析,分解连接,等等).而文档处理事务又会触发经一步的集群事务.集群事务,后续又会触发导出变化了的文档到服务系统中的事务.

通知和数据库的触发器相似.但是与数据库触发器不同的是,他们不能用来维护数据库的数据完整性.因为触发的观察者是从一个触发的写操作中运行一个独立的事务,所以触发的写操作和被触发的观察者写操作不是原子的.通知旨在帮助构建一个增量的计算而不是用来维护数据的完整性.

相对于复杂语义的重叠触发.这种语义和意图的不同使观察者行为更加容易被理解.Percolator应用由少量观察者组成----google的索引系统大约有10个观察者.每个观察者明确地在worker的main()函数的中构建.所以worker是知道哪些观察者是活跃的.几个观察者一起观察同一个column是可行的,但是我们避免了这个特性,所以我们很清除哪个观察者会在一个特殊的column被写入后运行.用户需要担心一下通知的无限循环,Percolator不会阻止这种事情;用户会通过构建一系列观察者的典型结构来避免无限循环.



猜你喜欢

转载自bucketli.iteye.com/blog/1297739