读取从机,读写主机设置

San*_*thi 21 python mysql sqlalchemy flask flask-sqlalchemy

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

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

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

zzz*_*eek 30

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

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

@with_slave
def my_view(...):
   # ...
Run Code Online (Sandbox Code Playgroud)

假设你有一个Session和一些引擎设置,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
Run Code Online (Sandbox Code Playgroud)

这个想法是调用Session(bind=slave)调用注册表来获取当前线程的实际Session对象,如果它不存在则创建它 - 但是由于我们传递了一个参数,scoped_session将声明我们在这里创建的Session是绝对是全新的.

您将其指向所有后续SQL的"从属".然后,当请求结束时,您将确保您的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
Run Code Online (Sandbox Code Playgroud)

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

  • Thnx zzzeek对此有很大帮助.感谢sqlalchemy所有令人敬畏的工作. (2认同)