在datastore中使用最终一致性读取

标签:Google App Engine, Python

昨天Google App Engine Team发了一篇《Read Consistency & Deadlines: More control of your Datastore》,不过服务器挂了3天,没法及时写上,所以今天再补上。

SDK 1.3.2发布时曾提到了强一致性和最终一致性读取,但是没有讲述细节,而这篇文章则阐述了其区别。
默认情况下,datastore是在一个主存储区域进行更新和查询的,这个区域代表最新的数据,访问这个区域则表示使用强一致性。而主存储区域有可能变得暂时不可用(估计是并发数过多或硬件故障),这就会导致查询超时或失败。
而实际上datastore是分布式的,它还有很多副存储区域。这些副存储区域的数据比主存储区域晚几百毫秒至数秒,因而不是最新的。如果你的应用不需要这种强一致性,就可以在主存储区域不可用时,让datastore读取其他存储区域的副本,而这就称为最终一致性读取。

Datastore为了保证向下兼容性,仍然默认使用强一致性,并且在事务中是完全一致性的(即保证事务中所有读取在事务结束时都是最新的版本);但现在你可以使用最终一致性读取,对Python而言,方法就是在fetch或get时传递一个rpc对象:
rpc = db.create_rpc(deadline=5, read_policy=db.EVENTUAL_CONSISTENCY)
results = Employee.all().fetch(10, rpc=rpc)
值得注意的有3点:
  1. rpc对象是不可重用的,每一次fetch都要传递一个新的rpc对象。
  2. deadline参数是超时设置,可以接受一个0~30秒之间的浮点数,这个参数可以让查询不至于影响整个handler超时。
  3. 还有一个callback参数,在rpc结束后就会被调用。
在我的初步测试中,我用了一个不经常更改的模型,所以主存储区域不可用的概率是很低的。基本上2种方法的性能没有太多差异,而创建rpc对象会多使用1~2ms的时间。此外,如果设置了deadline参数(不为None),那么性能还会继续降低。看来只在经常更新的模型上使用最终一致性读取比较合适。

此外我发现put操作也可以带rpc参数,不过read_policy应该是无效的,主要是使用deadline和callback。

顺带一提,我还使用了asynctools,要支持新读取策略的话需要修改一下(import我就省略了,自己加吧;代码我是看Google的源码自己研究的,也不保证以后一直有效)。
先是datastore.py,create_rpc需要加个参数:
def create_rpc(deadline=None, callback=None, read_policy=STRONG_CONSISTENCY):
    return DatastoreRPC('datastore_v3', deadline, callback, read_policy)
接着是__init__.py,DatastoreTask的__init__方法也加上read_policy参数:
from google.appengine.api.datastore import DatastoreRPC

class DatastoreTask(RpcTask):

    def __init__(self,  deadline=None, callback=None, read_policy=STRONG_CONSISTENCY, **kw):

        self._results = []
        self._exception = []

        rpc = datastore.create_rpc(deadline=deadline, callback=callback, read_policy=read_policy)
        rpc.callback = self.rpc_callback(rpc, self._results, self.exception, callback=callback)
        super(DatastoreTask, self).__init__(rpc, **kw)
最后在你自己的代码里,用这样的方法创建即可:
QueryTask(query, limit=10, read_policy=EVENTUAL_CONSISTENCY)


2011年1月7日更新:
Google已经建议用db.create_config()函数取代db.create_rpc()了(实际上在访问数据库时,SDK会自动将rpc对象转换成config对象),接口基本是一致的,但是config对象可以重用,而rpc对象不可重用。

0条评论 你不来一发么↓

    想说点什么呢?