Перезапись get_by_key_name () для использования memcache - PullRequest
1 голос
/ 20 января 2012

Имеет ли смысл перезаписывать get_by_key_name другой функцией, которая использует memcache для сохранения запросов базы данных? Как ниже? В этом примере ключ memcache состоит только из сущности key_name, чтобы сделать его простым ...

class Entity(db.Model):
   @classmethod
   def get_by_key_name(cls, key_name):
     entity = memcache.get(key_name)
     if entity is not None:
       return entity
     else:
       entity = cls.__super__.get_by_key_name(key_name)
       memcache.add(key_name, entity, 3600)
       return entity

Ответы [ 4 ]

2 голосов
/ 24 января 2012

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

2 голосов
/ 20 января 2012

Имеет ли это смысл, зависит от:

  1. Является ли производительность вашего приложения недостаточно хорошей (какова ваша цель по производительности и как вы ее измеряете?)
  2. запрос к базе данных действительно является узким местом для вашего приложения (проблема производительности может на самом деле лежать в другом месте, о котором вы не узнаете, пока не профилируете)
  3. Является ли запрос memcache на самом деле быстрее, чем кэш базы данных (если есть)и на сколько (повторные запросы к одной и той же записи (записям) из вашей базы данных в любом случае, вероятно, будут поступать из ОЗУ)
  4. шаблон запроса вашего приложения (на самом деле вы выполняете больше работы для каждого запроса, если запись неуже в memcache, поэтому, если большинство ваших запросов уникальны, это может быть даже медленнее! - кеш базы данных также может быть достаточным)

Возможно, кто-то уже измерил некоторые из них дляGoogle App Engine, но № 4 все еще будет тем, что вам нужно выяснить для вашего приложения.Другими словами: на самом деле не вопрос, на который можно ответить без дополнительных данных.Однако, надеюсь, я дал вам несколько мест для начала расследования.

В зависимости от того, насколько хорошо вы знаете базы данных и SQL, также возможно, что вы могли бы ускорить запросы, просто лучше используя базу данных, не добавляяусложнение кэширования в вашем приложении (на самом деле не достаточно разбираетесь в GAE, чтобы знать, сколько из этого он позволяет вам делать).

Что касается предложенной вами реализации, это не выглядит необоснованным, поскольку get_by_key_name()это уже метод класса.Вы можете попытаться внедрить свой новый метод непосредственно в существующий класс Model (или заменить весь свой подкласс обратно в модуль), чтобы вам не пришлось изменять код, который использует Model.Но это имеет свою опасность.Конечно, то, что у вас есть, достаточно хорошо, чтобы вы могли провести несколько тестов, чтобы увидеть, действительно ли это помогает.

1 голос
/ 21 января 2012

Идея кэширования хороша (хотя, как показали другие, вы должны быть осторожны с аннулированием).Однако я бы не стал переопределять get_by_name () для этого.Я бы написал отдельные функции для обработки кэширования, чтобы читатели вашего кода могли ясно видеть, что вы не используете стандартную функцию get_by_name ().Также обратите внимание, что get_by_name () - это действительно тонкий слой поверх get () с объектом Key - вам, вероятно, следует предложить и этот API.

0 голосов
/ 21 января 2012

Я также использовал memcache для кэширования сущностей, и я думаю, что это хорошая идея для ускорения операций с БД.Я решил добавить метод get_key в модель класса, потому что прокси memcache / datastore можно использовать во многих классах.

Определение общего прокси-сервера базы данных memcache

#mydb.py
def get_cache(keys):
    """ not support async operation
    because Async operation conflict with cache logiclly 
    """
    if not isinstance(keys, list) and not isinstance(keys, tuple):
        keys = [keys]

    keys = [str(k) for k in keys]
    results = memcache.get_multi(keys) #@UndefinedVariable
    keys_not_in_cache = [k for k in keys if k not in results] 

    if keys_not_in_cache:
        values = db.get([db.Key(k) for k in keys_not_in_cache])
        results_from_db = dict(zip(keys_not_in_cache, values))
        results.update(results_from_db)
        memcache.set_multi(results_from_db) #@UndefinedVariable

    return [results[k] for k in keys]        

def put_cache(values):
    """ Not support async operation
    """
    if not isinstance(values, list):
        values = [values]

    db.put(values)

    keys_str = [(str(k.key()), k) for k in values]
    memcache.set_multi(dict(keys_str)) #@UndefinedVariable    

def delete_cache(values):
    """ Not Support Async Operation
    """
    if not isinstance(values, list):
        values = [values]

    db.delete(values)

    keys_str = [str(k.key()) for k in values]
    memcache.delete_multi(keys_str) #@UndefinedVariable

Пример использования

# for the class want to use this feature
class User(db.Model):
    email = db.EmailProperty()

    @classmethod
    def get_key(cls, email):
        return db.Key.from_path(cls.__name__, email)

# Using memcache db proxy
user_keys = [User.get_key(USER_EMAIL_ADDRESS) for USER_EMAIL_ADDRESS in ALL_USER_EMAIL_ADDRESS]
users = mydb.get_cache(user_keys)
...