You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PostgreSQL只读副本读请求强制:属应用层任务?Django/Flask自动方案?

嘿,我来帮你把这两个问题讲清楚,结合Django和Flask的实际用法来给你梳理:


一、Django/Flask如何自动路由读请求到PostgreSQL只读副本

Django 原生实现自动读写分离

Django其实自带了读写分离的支持,完全不用手动每次指定连接,框架会自动根据操作类型路由请求,配置步骤如下:

  1. 在settings.py里配置主库和只读副本的数据库信息:
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'your_db_name',
        'USER': 'main_db_user',
        'PASSWORD': 'main_db_pass',
        'HOST': 'main-db-host',
        'PORT': '5432',
    },
    'read_replica': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'your_db_name',  # 和主库同名,因为是副本
        'USER': 'read_only_user',
        'PASSWORD': 'read_only_pass',
        'HOST': 'replica-db-host',
        'PORT': '5432',
        'TEST': {
            'MIRROR': 'default',  # 测试环境复用主库的测试数据,避免重复初始化
        }
    }
}
  1. 写一个数据库路由类,告诉Django怎么分配读写请求
    在项目根目录创建routers.py文件,写入路由逻辑:
class ReadReplicaRouter:
    def db_for_read(self, model, **hints):
        # 所有读操作优先走只读副本
        return 'read_replica'

    def db_for_write(self, model, **hints):
        # 所有写操作必须走主库
        return 'default'

    def allow_relation(self, obj1, obj2, **hints):
        # 允许主库和副本之间的关联关系(比如外键)
        allowed_dbs = ('default', 'read_replica')
        if obj1._state.db in allowed_dbs and obj2._state.db in allowed_dbs:
            return True
        return None

    def allow_migrate(self, db, app_label, model_name=None, **hints):
        # 只在主库执行数据库迁移,副本不需要做迁移
        return db == 'default'
  1. 在settings.py里注册这个路由类:
DATABASE_ROUTERS = ['your_project_name.routers.ReadReplicaRouter']

配置完成后,Django会自动处理:Model.objects.get()、filter()、all()这类读操作会自动用read_replica连接;save()、create()、delete()这类写操作则走default主库。如果遇到特殊场景(比如刚写完数据需要立即读取最新值,避免副本同步延迟),可以手动指定走主库:

# 强制从主库读取数据
latest_obj = Model.objects.using('default').get(pk=1)

Flask 实现自动读写分离

Flask本身没有原生的读写分离功能,但可以通过Flask-SQLAlchemy的绑定功能或者第三方库来实现自动路由,这里给你两种常用方案:

方案1:基于Flask-SQLAlchemy的自定义路由

  1. 先配置多个数据库连接:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
# 默认连接主库
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://main_user:main_pass@main-db-host:5432/your_db'
# 绑定只读副本
app.config['SQLALCHEMY_BINDS'] = {
    'read_replica': 'postgresql://read_user:read_pass@replica-db-host:5432/your_db'
}
db = SQLAlchemy(app)
  1. 封装自动读路由的逻辑
    可以自定义一个Query类,让读操作默认使用副本连接:
from sqlalchemy.orm import Query

class ReadReplicaQuery(Query):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        # 读操作默认绑定副本连接
        self = self.with_session(db.get_engine(app, bind='read_replica').connect())

# 定义抽象基类,让所有模型继承它
class BaseModel(db.Model):
    __abstract__ = True
    query_class = ReadReplicaQuery

# 你的业务模型继承BaseModel
class YourModel(BaseModel):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))

这样,平时的读操作(比如YourModel.query.all())会自动走副本;写操作直接用默认的db.session就会走主库:

# 写操作,自动走主库
new_item = YourModel(name="test")
db.session.add(new_item)
db.session.commit()

如果需要强制读主库,手动指定连接即可:

# 强制从主库读取
item = db.session.query(YourModel).with_session(db.get_engine(app).connect()).get(1)

方案2:使用第三方库sqlalchemy-replication

这个库专门用来处理SQLAlchemy的主从读写分离,配置更省心:

from sqlalchemy import create_engine
from sqlalchemy_replication import load_balanced_session, MasterSlaveConnectionPool

# 创建主从连接池,主库地址写在前面,后面跟副本地址
engine = create_engine(
    'postgresql://main_user:main_pass@main-db-host:5432/your_db',
    poolclass=MasterSlaveConnectionPool,
    slaves=[
        'postgresql://read_user:read_pass@replica-db-host:5432/your_db'
    ]
)

# 创建自动路由的session
Session = load_balanced_session(bind=engine)
session = Session()

# 读操作自动走副本,写操作自动走主库
all_items = session.query(YourModel).all()  # 走副本
session.add(YourModel(name="new test"))  # 走主库
session.commit()

二、强制读请求路由至只读副本是否属于应用层任务?

答案是肯定的,这属于应用层的职责,原因主要有这几点:

  1. 数据库主从同步只是完成数据层面的复制,它不会主动帮应用分流请求;
  2. 应用层最清楚当前操作的属性:是读还是写,是否需要读取最新数据(比如刚写完就读的场景,必须走主库避免同步延迟),可以做更灵活的定制;
  3. 虽然有些中间件(比如PgBouncer)能做简单的读写分流,但灵活性远不如应用层控制——比如无法针对特定模型、特定请求做个性化路由。

至于你提到的“自动管理连接路由,无需手动指定”,Django原生就支持这个功能;Flask虽然需要额外配置或借助扩展,但也能实现不用每次手动选连接的效果,框架或封装的逻辑会自动根据操作类型选择对应的数据库连接。

内容的提问来源于stack exchange,提问作者Subtubes

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 06:52:55