Как использовать разные базы данных в зависимости от статуса входа в Django? - PullRequest
4 голосов
/ 15 декабря 2011

По сути, чтобы не беспокоиться о задержке репликации, если пользователь вошел в систему, мы хотим, чтобы он считывал / записывал в мастер; но если пользователь не вошел в систему, мы хотим, чтобы они читали из реплики и записывали в мастер. Возможно ли это с помощью роутеров Django?

Ответы [ 3 ]

5 голосов
/ 15 декабря 2011

Это может быть плохой практикой, но в любой точке вашего кода, если вы используете Django> = 1.2, с методом using() и ключевым словом using, вы имеете полный контроль над тем, какая база данных выберет ваш QuerySetsи сохраните и удалите.

Например, для QuerySet:

>>> MyModel.objects.using('replica').all()

или для сохранения объекта:

>>> my_object.save(using='master')

Так что вы всегда можете сделать что-то вродеэто в представлении:

query_set = MyModel.objects.all()

if request.user.is_authenticated():
    query_set = query_set.using('master')

Но это может стать очень громоздким, и если вы решите пойти по этому пути, вы должны искать более элегантные способы смягчить его. Сигналы , middlewares и / или менеджеры моделей приходят на ум.

См. Ручной выбор базы данных из Djangoдокументация.

1 голос
/ 15 декабря 2011
class MasterSlaveRouter(object):
    """A router that sets up a simple master/slave configuration"""

    def db_for_read(self, model, **hints):
        "Point all read operations to a random slave"
        return random.choice(['slave1','slave2'])

    def db_for_write(self, model, **hints):
        "Point all write operations to the master"
        return 'master'

    def allow_relation(self, obj1, obj2, **hints):
        "Allow any relation between two objects in the db pool"
        db_list = ('master','slave1','slave2')
        if obj1._state.db in db_list and obj2._state.db in db_list:
            return True
        return None

    def allow_syncdb(self, db, model):
        "Explicitly put all models on all databases."
        return True

Из документов Django

Это может быть, если вы действительно хотите сделать это как упражнение, но я бы не советовал его, если он будет запущен в производство.

Вы просите свою базу данных выполнить репликацию для вас, что является правильным способом сделать это.

Затем в своем приложении вы в основном говорите, что хотите перейти в середину этой репликации и записать ее в ведомое устройство, а затем прочитать с мастера; другими словами, вы пытаетесь использовать репликацию так же, как и кластер. Это может привести только к плохим вещам в будущем; параллелизм для одного будет проблемой. Пул подключений - другой, а целостность данных - третий.

Другой подход к сокращению времени отклика - если у пользователя есть учетная запись, загрузите его информацию в быстрый бэкэнд кеша, такой как redis или couchdb, - в зависимости от ваших предпочтений ключ / значение по сравнению с хранилищами на основе документов.

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

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

Возможно, да.Взгляните на проект django-multi-db .Он предоставляет простой MasterSlaveRouter, который практически идентичен тому, который указан в документации Django .Он также предоставляет PinningMasterSlaveRouter, который решает вашу конкретную проблему.Из их документации:

В некоторых приложениях задержка между ведущим устройством, получающим запись, и его репликацией на ведомые устройства является достаточной, чтобы вызвать несогласованность для конечного пользователя.Например, представьте сценарий с задержкой репликации в 1 секунду.Если пользователь делает сообщение на форуме (ведущему), а затем перенаправляется на полностью отрендеренное представление этого сообщения (от подчиненного) через 500 мс, представление не будет выполнено.Если это проблема в вашем приложении, рассмотрите возможность использования multidb.PinningMasterSlaveRouter.Этот маршрутизатор работает в сочетании с multidb.middleware.PinningRouterMiddleware, чтобы гарантировать, что после записи в базу данных по умолчанию будущие чтения из того же пользовательского агента направляются в базу данных по умолчанию в течение настраиваемого промежутка времени.

PiningMasterSlaveRouter выберет мастер для чтения только в том случае, если он считает, что во время этого запроса была произведена запись в базу данных.Вероятно, это лучшая стратегия, которая всегда читает с мастера для вошедших в систему пользователей, но если вы уверены, что это то, чего вы хотите, то все еще возможно достичь с небольшим количеством дополнительного промежуточного программного обеспечения:

class SelectMasterForLoggedInUsers(object):
    def process_response(self, request, response):
        if request.user.is_authenticated():
            response._db_write = True
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...