Как получить один DB URI для чтения и один для чтения-записи [дублировать]

Существует также df2 <- count(x, c('Year','Month')) (пакет plyr)

20
задан Sandeep Chayapathi 20 January 2012 в 23:13
поделиться

2 ответа

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

Немного менее волшебный подход, который также устанавливает область использования более явным образом, я использовал декоратор на вызовах вида (что бы они ни называли в 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

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

27
ответ дан mjallday 21 August 2018 в 12:23
поделиться
  • 1
    Thnx zzzeek это очень помогает. Престижность всей удивительной работы по sqlalchemy. – Sandeep Chayapathi 24 January 2012 в 17:04
  • 2
    Rad комментарий, отличные примеры кода тоже! Было бы неплохо, если бы sqlalchemy каким-то образом выполнял анализ запросов и маршрутизировал автоматически, но в мире, где запрос может вызвать таблицу tmp или другую операцию записи в результате того, что, по-видимому, обычно читается только, что потребует чего-то вроде запроса план запроса из бэкэнд перед отправкой запроса и будет более сложным, чем это было бы полезно для большинства случаев. – Dave Rawks 13 May 2012 в 19:40
  • 3
    у нас есть «анализ запросов». вариант, хотя он требует, чтобы вы сами составляли анализ. Горизонтальная система очертания иллюстрирует пример такого метода, см. docs.sqlalchemy.org/en/rel_0_7/orm/extensions/… . – zzzeek 16 May 2012 в 16:07
  • 4
    Крюки, чтобы иметь функцию обратного вызова для решения вопроса о том, как очертить запросы, являются точными и все, но действительно что-то вроде правильной классификации проверки запросов для разделения операций чтения ops от write ops является чем-то, что выходит за рамки большинства пользователей sqlalchemy. На самом деле я надеюсь, что в какой-то момент в ближайшем будущем есть некоторые основные реализации этих стратегий, доступных в качестве шаблона в SA. – Dave Rawks 25 May 2012 в 17:41
  • 5
    В некоторых случаях анализ запросов для выбора сеанса будет плохой идеей. Вы хотите, чтобы ваши чтения выполнялись в той же транзакции, что и ваши записи, в случае, если ваши чтения повлияли на ваши записи. Представьте, что в том же вызове вы удаляете строку таблицы, а затем возвращаете сумму столбца всех записей этой таблицы. Если вы используете две разные транзакции, сумма будет содержать только что удаленную строку, поскольку другая транзакция еще не выполнена. – Patrick Valsecchi 12 May 2016 в 06:01

Или мы можем попробовать другой путь. Например, мы можем объявить два разных класса со всеми атрибутами экземпляра одинаковыми, но атрибут класса __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'    
0
ответ дан andy 21 August 2018 в 12:23
поделиться
  • 1
    можете ли вы улучшить свой ответ, предоставив пример создания, определения и использования двух баз данных с различными возможностями чтения и записи – anand tripathi 13 February 2018 в 07:50
Другие вопросы по тегам:

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