用遍历查询对象来节省datastore查询时间

标签:Google App Engine, Python

GAE SDK 1.4.0版发布时,我曾注意到“遍历查询结果时,datastore将异步预获取结果”这个变化。
以前文档里曾指出遍历查询对象是个很低效的方法,所以我一直都不这样用。今天特意测试了一番,发现它原来并不低效。

from time import time
from google.appengine.ext import db
from google.appengine.api import apiproxy_stub_map

db_count = 0
def before_db(service, call, request, response):
	global db_count
	db_count += 1
apiproxy_stub_map.apiproxy.GetPreCallHooks().Append('before_db', before_db, 'datastore_v3')

class TestModel(db.Model):
	pass

COUNT = 1000

def testIterator():
	t = time()
	i = 0
	for e in TestModel.all():
		i += 1
		if i == COUNT:
			return time() - t

print 'testIterator'
t = testIterator()
print t
print db_count
print t / db_count

def testCursor(size_per_fetch):
	t = time()
	q = TestModel.all()
	for i in xrange(COUNT / size_per_fetch):
		q.fetch(size_per_fetch)
		q.with_cursor(q.cursor())
	return time() - t

db_count = 0
print '\ntestCursor 10'
t = testCursor(10)
print t
print db_count
print t / db_count

db_count = 0
print '\ntestCursor 20'
t = testCursor(20)
print t
print db_count
print t / db_count

db_count = 0
print '\ntestCursor 50'
t = testCursor(50)
print t
print db_count
print t / db_count
结果:
testIterator
0.57021689415
51
0.0111807234147

testCursor 10
1.20552110672
100
0.0120552110672

testCursor 20
0.821892976761
50
0.0164378595352

testCursor 50
0.533807039261
20
0.026690351963
可以看出,迭代查询对象时,每次会fetch 20个结果,且会多一次prefetch。而同样是一次fetch 20个结果,它比使用cursor要快44%。不过更高效的方法当然是一次获取更多数据,来减少数据库访问次数。

读了SDK的源码后发现,google\appengine\api\datastore_file_stub.py中定义了“_BATCH_SIZE = 20”,并且在_Dynamic_Next方法中直接使用了这个值。
不过云端用的不是datastore_file_stub,所以只能用hook替换掉这个_Dynamic_Next方法,来使用自定义的batch size。
而调用端是用Batcher这个类来实现prefetch的,由于太复杂,暂时也没想出什么用它来代替cursor来分页的办法。

0条评论 你不来一发么↓

    想说点什么呢?