GAE数据库压力测试

标签:Google App Engine, Python

今天在JavaEye看到一篇《NoSQL数据库探讨之一 - 为什么要用非关系数据库?》,出于好奇就测试了一下GAE的并发性能。

用的方法是产生大量异步Urlfetch,然后看响应时间和数据库时间。
测试的这个模型有约32万个实体,27M空间(含索引),每个实体只有一个字符串属性。

因为测试结果波动较大,我就不列出了,只给出结论:
异步Urlfetch的时间影响很小,每个约0.1ms,所以基本不对测试造成影响。
直接返回一个'0',在10秒内约可支持1600多个并发。1600个共用7.92秒,平均每个请求4.95ms。
插入1个实体,在10秒内约可支持1100多个并发。1000个共用9.60秒,平均每个请求9.60ms。数据库API时间为47.68秒。
查询10个实体(一般查询量都比较大),在10秒内约可支持1000个并发。1000个共用10.05秒,完成990个,平均每个请求10.2ms。数据库API时间为60.14秒。
更新1个实体,在10秒内约可支持200个并发。200个共用9.15秒,完成198个,平均每个请求46.2ms。数据库API时间为27.50秒。
删除1个实体,在10秒内约可支持200个并发。200个共用8.10秒,平均每个请求40.5ms。数据库API时间为29.90秒。

可以看出,插入的效率很高,可能是因为只需要在一台服务器插入,复制所需时间不计算在内;而更新和删除需要保证所有备份的一致性,同步需要一些时间。
从查询的效率来看,并发大概在100个/秒左右。按一个论坛平均每个页面4次查询来计算,每人每分钟浏览2个网页,约可以支撑1500人同时在线。
这个速度有点差强人意了…要知道Discuz!和PHPWind可是用MySQL支持数万人同时在线的。

服务器端测试代码:
from random import random
from time import time
from wsgiref.handlers import CGIHandler
from google.appengine.api import apiproxy_stub_map
from google.appengine.ext import webapp
from google.appengine.ext import db

result = [0, 0]

def before_db(service, call, request, response):
  result[0] = time()

def after_db(service, call, request, response):
  result[1] = time()

apiproxy_stub_map.apiproxy.GetPreCallHooks().Append('before_db', before_db, 'datastore_v3')
apiproxy_stub_map.apiproxy.GetPostCallHooks().Push('after_db', after_db, 'datastore_v3')

class TestModel(db.Model):
  content = db.StringProperty()

class Nothing(webapp.RequestHandler):
  def get(self):
    self.response.out.write('0')

class Add(webapp.RequestHandler):
  def get(self):
    self.response.headers['Content-Type'] = 'text/plain'
    size = int(self.request.get('size'))
    if size < 1:
      raise Exception

    entities = []
    for i in xrange(size):
      entity = TestModel(content=`random()`)
      entities.append(entity)

    db.put(entities)

    self.response.out.write(`result[1] - result[0]`)


class Fetch(webapp.RequestHandler):
  def get(self):
    self.response.headers['Content-Type'] = 'text/plain'
    size = int(self.request.get('size'))
    if size < 1:
      raise Exception

    entities = TestModel.all().fetch(size)
    assert len(entities) == size

    self.response.out.write(`result[1] - result[0]`)


class Update(webapp.RequestHandler):
  def get(self):
    self.response.headers['Content-Type'] = 'text/plain'
    size = int(self.request.get('size'))
    if size < 1:
      raise Exception

    entities = TestModel.all().fetch(size)
    assert len(entities) == size
    for entity in entities:
      entity.content = `random()`
    db.put(entities)

    self.response.out.write(`result[1] - result[0]`)


class Del(webapp.RequestHandler):
  def get(self):
    self.response.headers['Content-Type'] = 'text/plain'
    size = int(self.request.get('size'))
    if size < 1:
      raise Exception

    entities = TestModel.all(keys_only=True).fetch(size)
    assert len(entities) == size
    db.delete(entities)

    self.response.out.write(`result[1] - result[0]`)


application = webapp.WSGIApplication([('/test/nothing', Nothing),
                                      ('/test/add', Add),
                                      ('/test/fetch', Fetch),
                                      ('/test/update', Update),
                                      ('/test/del', Del)])

def main():
  CGIHandler().run(application)

if __name__ == '__main__':
  main()
控制台测试代码:
from google.appengine.api import urlfetch
from time import time

result = []
def handle_result(rpc):
	def handle():
		try:
			result.append(float(rpc.get_result().content))
		except:
			pass
	return handle
	
rpcs = []

t = time()
for i in xrange(1000):
	rpc = urlfetch.create_rpc(deadline=10)
	rpc.callback = handle_result(rpc)
	urlfetch.make_fetch_call(rpc, 'http://你的应用程序地址/test/fetch?size=10') # 这里的URL按需要更改
	rpcs.append(rpc)
t2 = time()

for rpc in rpcs:
	rpc.wait()
t3 = time()

print t2 - t
print t3 - t
s = sum(result)
l = len(result)
print s
print l
print s / l

0条评论 你不来一发么↓

    想说点什么呢?