Подтвердить что ты не робот

Чтение подчиненного устройства, чтение-запись

У меня есть Flask, SQLAlchemy webapp, который использует один сервер mysql. Я хочу расширить базу данных, чтобы иметь подчиненный сервер только для чтения, так что я могу распространять считывания между ведущим и подчиненным, продолжая писать на сервер master db.

Я просмотрел несколько вариантов, и я считаю, что не могу сделать это с помощью простой SQLAlchemy. Вместо этого я планирую создать 2 дескриптора базы данных в моем webapp, по одному для серверов master и slave db. Затем, используя простое случайное значение, используйте либо дескриптор master/slave db для операций "SELECT".

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

4b9b3361

Ответ 1

У меня есть пример того, как это сделать в моем блоге на http://techspot.zzzeek.org/2012/01/11/django-style-database-routers-in-sqlalchemy/. В основном вы можете улучшить сеанс, чтобы он выбирал из основного или подчиненного на основе запроса по запросу. Один из возможных сбоев в этом подходе состоит в том, что если у вас есть одна транзакция, которая вызывает шесть запросов, вы можете использовать оба подчиненных в одном запросе... но там мы просто пытаемся имитировать функцию Django:)

Немного менее волшебный подход, который также устанавливает область использования более явным образом, я использовал декоратор в виде callables (независимо от того, что они вызываются в Flask), например:

@with_slave
def my_view(...):
   # ...

with_slave будет делать что-то вроде этого, предполагая, что у вас есть сеанс и установлены некоторые двигатели:

master = create_engine("some DB")
slave = create_engine("some other DB")
Session = scoped_session(sessionmaker(bind=master))

def with_slave(fn):
    def go(*arg, **kw):
        s = Session(bind=slave)
        return fn(*arg, **kw)
    return go

Идея состоит в том, что вызов Session(bind=slave) вызывает реестр для получения фактического объекта Session для текущего потока, создавая его, если он не существует, однако, поскольку мы передаем аргумент, scoped_session будет утверждать, что сеанс мы здесь совершенно определенно.

Вы указываете его на "подчиненный" для всех последующих SQL. Затем, когда запрос завершен, вы убедитесь, что ваше приложение Flask вызывает Session.remove(), чтобы удалить реестр для этого потока. Когда реестр будет использоваться в том же потоке, это будет новый сеанс, связанный с "мастером".

Или вариант, вы хотите использовать "ведомый" только для этого вызова, это "безопаснее", поскольку он восстанавливает любое существующее привязку к сеансу:

def with_slave(fn):
    def go(*arg, **kw):
        s = Session()
        oldbind = s.bind
        s.bind = slave
        try:
            return fn(*arg, **kw)
        finally:
            s.bind = oldbind
    return go

Для каждого из этих декораторов вы можете отменить все, связать сессию с "подчиненным", где декоратор помещает его в "master" для операций записи. Если бы вам понадобилось случайное ведомое устройство в этом случае, если у Flask было какое-то "событие начала запроса", вы могли бы установить его в этой точке.

Ответ 2

Или, мы можем попробовать другой путь. Например, мы можем объявить два разных класса со всеми атрибутами экземпляра одинаковыми, но атрибут класса __bind__ отличается. Таким образом, мы можем использовать класс rw для чтения/записи и класса r для чтения только для чтения.:)

Я думаю, что этот способ проще и надежнее.:)

Мы объявляем две модели db, потому что мы можем иметь таблицы в двух разных db с одинаковыми именами. Таким образом, мы также можем обойти ошибку "extend_existing", когда две модели с тем же __ tablename __.

Вот пример:

app = Flask(__name__)
app.config['SQLALCHEMY_BINDS'] = {'rw': 'rw', 'r': 'r'}
db = SQLAlchemy(app)
db.Model_RW = db.make_declarative_base()

class A(db.Model):
    __tablename__ = 'common'
    __bind_key__ = 'r'

class A(db.Model_RW):
    __tablename__ = 'common'
    __bind_key__ = 'rw'