读取从机,读写主机设置

时间:2012-01-20 21:13:02

标签: python mysql sqlalchemy flask flask-sqlalchemy

我有一个使用单个mysql服务器的Flask,SQLAlchemy webapp。我想扩展数据库设置以使其具有只读从​​属服务器,这样我就可以在主服务器和从服务器之间传播读取,同时继续写入主数据库服务器。

我已经看了几个选项,我相信我不能用简单的SQLAlchemy做到这一点。相反,我计划在我的webapp中创建2个数据库句柄,每个句柄用于主数据库服务器和从数据库服务器。然后使用简单的随机值使用主/从db句柄进行“SELECT”操作。

但是我不确定这是否是使用SQLAlchemy的正确方法。关于如何解决此问题的任何建议/提示?提前谢谢。

2 个答案:

答案 0 :(得分:30)

我在http://techspot.zzzeek.org/2012/01/11/django-style-database-routers-in-sqlalchemy/的博客上有一个如何执行此操作的示例。基本上,您可以增强会话,以便它在逐个查询的基础上从主服务器或从服务器中进行选择。这种方法的一个潜在故障是,如果你有一个调用六个查询的事务,你可能最终在一个请求中使用两个从属....但是我们只是试图模仿Django的特性:)

一种稍微不那么神奇的方法,它也更明确地确定了我使用的范围是视图callables的装饰器(无论它们在Flask中被调用),如下所示:

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

with_slave会做这样的事情,假设你有一个Session并设置了一些引擎:

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的“slave”。然后,当请求结束时,您将确保Flask应用程序正在调用Session.remove()以清除该线程的注册表。当注册表接下来在同一个线程上使用时,它将是一个绑定回“master”的新Session。

或者一个变体,你想只为那个调用使用“slave”,这是“更安全”,因为它将任何现有的bind恢复回Session:

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

对于每个这些装饰器,你可以反转一些东西,将Session绑定到一个“slave”,装饰器把它放在“master”上进行写操作。如果在这种情况下你想要一个随机的奴隶,如果Flask有某种“请求开始”事件你可以在那时设置它。

答案 1 :(得分:0)

或者,我们可以尝试另一种方式。例如,我们可以声明两个不同的类,所有实例属性相同但__bind__类属性不同。因此,我们可以使用 rw 类进行读/写,使用 r 类进行只读。 :)

我认为这种方式更简单可靠。 :)

我们声明了两个db模型,因为我们可以在两个不同的db中使用相同名称的表。这样,当两个具有相同 __ tablename __ 的模型时,我们也可以绕过'extend_existing'错误。

以下是一个例子:

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'