Что лучший способ состоит в том, чтобы сделать Модель AppEngine Memcaching?

В настоящее время мои модели кэшей приложения в кэш-памяти как это:

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()

16
задан David Underhill 21 July 2010 в 21:04
поделиться

3 ответа

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

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

Поэтому в целом любой метод работает примерно одинаково... но

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

.
4
ответ дан 30 November 2019 в 23:27
поделиться

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

1
ответ дан 30 November 2019 в 23:27
поделиться

Один из способов сделать это быстрее - превратить вашу модель в словарь и использовать встроенную функцию eval / repr в качестве (де) сериализатора - с осторожностью, конечно, как всегда с злой 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

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

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

### ПОДДЕЛКА МОДЕЛИ СУБЪЕКТА ###
{'status': u'admin ',' session_expiry ': None,' first_name ': u'Louis', 'last_name': u'Le Sieur ',' modified_by ': None,' password_hash ': u'a9993e364706816aba3e25717000000000000000', 'language': u'fr ',' created ': datetime.datetime (2010, 7, 18, 21, 50, 11, 750000),' modified ': Нет,' created_by ': Нет,' email ': (скрыто ) 'key': 'agdqZXJlZ2xlcgwLEgVMb2dpbhjmAQw', 'session_ref': None, '_cls': 'models.Login ',' groups ': [],' email___password_hash ': (скрыто)' subdomain ': datastore_types.Key.from_path (u'Subdomain', 229L, _app = u'jeregle '),' allowed ': [],' разрешения ': []}


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

1
ответ дан 30 November 2019 в 23:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: