代码之家  ›  专栏  ›  技术社区  ›  Koen Bok

做AppEngine模型Memcaching的最好方法是什么?

  •  16
  • Koen Bok  · 技术社区  · 16 年前

    目前,我的应用程序将模型缓存在memcache中,如下所示:

    memcache.set("somekey", aModel)
    

    但是尼克斯在 http://blog.notdot.net/2009/9/Efficient-model-memcaching 这表明首先将其转换为protobuffers效率更高。但在运行了一些测试后,我发现它的尺寸确实更小,但实际上 更慢的 (~10%).

    其他人有同样的经历还是我做错了什么?

    测试结果: http://1.latest.sofatest.appspot.com/?times=1000

    import pickle
    import time
    import uuid
    
    from google.appengine.ext import webapp
    from google.appengine.ext import db
    from google.appengine.ext.webapp import util
    from google.appengine.datastore import entity_pb
    from google.appengine.api import memcache
    
    class Person(db.Model):
     name = db.StringProperty()
    
    times = 10000
    
    class MainHandler(webapp.RequestHandler):
    
     def get(self):
    
      self.response.headers['Content-Type'] = 'text/plain'
    
      m = Person(name='Koen Bok')
    
      t1 = time.time()
    
      for i in xrange(int(self.request.get('times', 1))):
       key = uuid.uuid4().hex
       memcache.set(key, m)
       r = memcache.get(key)
    
      self.response.out.write('Pickle took: %.2f' % (time.time() - t1))
    
    
      t1 = time.time()
    
      for i in xrange(int(self.request.get('times', 1))):
       key = uuid.uuid4().hex
       memcache.set(key, db.model_to_protobuf(m).Encode())
       r = db.model_from_protobuf(entity_pb.EntityProto(memcache.get(key)))
    
    
      self.response.out.write('Proto took: %.2f' % (time.time() - t1))
    
    
    def main():
     application = webapp.WSGIApplication([('/', MainHandler)], debug=True)
     util.run_wsgi_app(application)
    
    
    if __name__ == '__main__':
     main()
    
    3 回复  |  直到 16 年前
        1
  •  4
  •   TFD    16 年前

    Memcache调用仍然使用protobuf或不使用protobuf来pickle对象。Pickle使用protobuf对象更快,因为它有一个非常简单的模型

    普通pickle对象比protobuf+pickle对象大,因此它们节省了Memcache上的时间,但是在进行protobuf转换时处理器的时间更多

    因此,一般来说,这两种方法的结果都差不多……但是

    您应该使用protobuf的原因是它可以处理模型版本之间的更改,而Pickle会出错。这个问题总有一天会困扰你的,所以最好早点解决

        2
  •  1
  •   Brian Slesinsky    16 年前

    pickle和protobufs在appengine中都很慢,因为它们是用纯Python实现的。我发现使用str.join这样的方法编写我自己的简单序列化代码往往更快,因为大部分工作都是用C完成的。但这只适用于简单的数据类型。

        3
  •  1
  •   Louis LC    16 年前

    一种更快的方法是将您的模型转换为字典,并使用本机eval/repr函数作为(反)序列化程序——当然要小心,就像使用邪恶的eval一样,但是考虑到没有外部步骤,这里应该是安全的。

    下面是一个类实体的例子,它正好实现了这一点。 你首先通过 fake = Fake_entity(entity) 然后您可以通过 memcache.set(key, fake.serialize()) . serialize()是对repr的本机dictionary方法的简单调用,如果需要,还可以添加一些内容(例如,在字符串的开头添加标识符)。

    要取回它,只需使用 fake = Fake_entity(memcache.get(key)) . 伪实体对象是一个简单的字典,其键也可以作为属性访问。您可以正常访问实体属性,但referenceproperty提供键而不是获取对象(这实际上非常有用)。您还可以使用fake.get()获取()实际实体,或者更有趣的是,更改它,然后使用fake.put()保存。

    它不适用于列表(如果您从查询中获取多个实体),但可以使用诸如“####假模型实体###”之类的标识符作为分隔符,通过连接/拆分函数轻松地进行调整。仅与db.Model一起使用,Expando需要小的调整。

    class Fake_entity(dict):
        def __init__(self, record):
            # simple case: a string, we eval it to rebuild our fake entity
            if isinstance(record, basestring):
                import datetime # <----- put all relevant eval imports here
                from google.appengine.api import datastore_types
                self.update( eval(record) ) # careful with external sources, eval is evil
                return None
    
            # serious case: we build the instance from the actual entity
            for prop_name, prop_ref in record.__class__.properties().items():
                self[prop_name] = prop_ref.get_value_for_datastore(record) # to avoid fetching entities
            self['_cls'] = record.__class__.__module__ + '.' + record.__class__.__name__
            try:
                self['key'] = str(record.key())
            except Exception: # the key may not exist if the entity has not been stored
                pass
    
        def __getattr__(self, k):
            return self[k]
    
        def __setattr__(self, k, v):
            self[k] = v
    
        def key(self):
            from google.appengine.ext import db
            return db.Key(self['key'])
    
        def get(self):
            from google.appengine.ext import db
            return db.get(self['key'])
    
        def put(self):
            _cls = self.pop('_cls') # gets and removes the class name form the passed arguments
            # import xxxxxxx ---> put your model imports here if necessary
            Cls = eval(_cls) # make sure that your models declarations are in the scope here
            real_entity = Cls(**self) # creates the entity
            real_entity.put() # self explanatory
            self['_cls'] = _cls # puts back the class name afterwards
            return real_entity
    
        def serialize(self):
            return '### FAKE MODEL ENTITY ###\n' + repr(self)
            # or simply repr, but I use the initial identifier to test and eval directly when getting from memcache
    

    我很欢迎在这方面进行速度测试,我认为这比其他方法要快得多。另外,如果您的模型在此期间发生了变化,您也不会有任何风险。

    下面是序列化假实体的外观示例。请特别查看datetime(已创建)以及引用属性(子域):

    ###假模型实体###


    就我个人而言,我还使用静态变量(比memcache快)在短期内缓存实体,并在服务器发生更改或其内存由于某种原因被刷新(事实上这种情况经常发生)时获取数据存储。