Как лучше всего использовать AppEngine Model Memcaching? - PullRequest
16 голосов
/ 20 февраля 2010

В настоящее время мое приложение кэширует модели в memcache следующим образом:

memcache.set("somekey", aModel)

Но пост Ника в http://blog.notdot.net/2009/9/Efficient-model-memcaching предполагает, что сначала преобразовать его в протобуферы намного эффективнее. Но после выполнения некоторых тестов я обнаружил, что он действительно меньше по размеру, но на самом деле медленнее (~ 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 ]

4 голосов
/ 01 марта 2010

Вызов Memcache все еще выбирает объект с использованием или без использования protobuf. Pickle быстрее работает с объектом protobuf, поскольку у него очень простая модель

Обычные объекты pickle больше, чем объекты protobuf + pickle, следовательно, они экономят время на Memcache, но при преобразовании протобуфа уходит больше процессорного времени

Поэтому, в общем, оба метода работают примерно одинаково ... но

Причина, по которой вы должны использовать protobuf, заключается в том, что он может обрабатывать изменения между версиями моделей, тогда как Pickle выдаст ошибку. Эта проблема укусит вас однажды, поэтому лучше справиться с ней раньше

1 голос
/ 20 июля 2010

Один из способов сделать это быстрее - это превратить вашу модель в словарь и использовать встроенную функцию eval / repr в качестве (de) сериализаторов - с осторожностью, конечно, как всегда для злого eval, но это должно бытьЗдесь безопасно, поскольку нет внешнего шага.

Ниже приведен пример реализации класса Fake_entity.Сначала вы создаете свой словарь с помощью fake = Fake_entity(entity), затем вы можете просто сохранить свои данные с помощью memcache.set(key, fake.serialize()).Serialize () - это простой вызов родного словарного метода repr, с некоторыми добавлениями, если вам нужно (например, добавить идентификатор в начале строки).

Чтобы получить его обратно, просто используйте fake = Fake_entity(memcache.get(key)).Объект Fake_entity - это простой словарь, ключи которого также доступны в качестве атрибутов.Вы можете получить доступ к свойствам вашей сущности в обычном режиме, за исключением того, что referenceProperties дают ключи вместо выборки объекта (что на самом деле весьма полезно).Вы также можете получить () фактическую сущность с помощью fake.get () или, что более интересно, изменить ее и затем сохранить с помощью fake.put ().

Она не работает со списками (если вы выбираете несколько сущностей)из запроса), но может быть легко отрегулирован с помощью функций соединения / разделения с использованием идентификатора, подобного '### FAKE MODEL ENTITY ###' в качестве разделителя.Используйте только с 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

Я бы приветствовал тесты скорости на этом, я бы предположил, что это намного быстрее, чем другие подходы.Кроме того, у вас нет никаких рисков, если ваши модели каким-то образом изменились за это время.

Ниже приведен пример того, как выглядит сериализованная поддельная сущность.Посмотрите на дату и время (созданные), а также на свойства ссылок (поддомен):

### FAKE MODEL ENTITY ###
{'status': u'admin ',' session_expiry ': None,'first_name ': u'Louis', 'last_name': u'Le Sieur ','ified_by': None, 'password_hash': u'a9993e364706816aba3e25717000000000000000 ',' language ': u'fr', 'созданный': datetime.datetime (2010, 7, 18, 21, 50, 11, 750000), «изменено»: нет, «создано»: нет, «электронная почта»: u'chou@glou.bou »,« ключ »:« agdqZXJlZ2xlcgwLEgVMb2dpbhjmAQw »,« session_ref »': None,' _cls ':' models.Login ',' groups ': [],' email___password_hash ': u'chou@glou.bou+a9993e364706816aba3e2571700000000000000000', 'subdomain': datastore_types.Key.from_path uname (, 229L, _app = u'jeregle '),' разрешено ': [],' permissions ': []}


Лично я также использую статические переменные (быстрее чем memcache) для кэширования своих сущностей в краткосрочной перспективеи получить хранилище данных, когда сервер изменился или его память по какой-то причине была очищена (что часто случается вфакт).

1 голос
/ 15 марта 2010

Обе команды pickle и protobufs работают медленно в App Engine, поскольку они реализованы на чистом Python.Я обнаружил, что написание моего собственного простого кода сериализации с использованием таких методов, как str.join, обычно происходит быстрее, поскольку большая часть работы выполняется на C. Но это работает только для простых типов данных.

...