django多数据库读写分离需配置databases定义主从库,自定义databaserouter实现db_for_read返回从库别名、db_for_write返回主库别名,并在database_routers中注册;allow_migrate须精准控制仅主库执行迁移,否则报错。

如何在 Django 中配置多个数据库并指定读写路由
Django 本身不自动区分读写,必须通过数据库路由(DatabaseRouter)显式控制每个查询发往哪个库。核心不是“加多个数据库”,而是让 select 走从库、insert/update/delete 走主库。
先在 settings.py 中定义两个数据库:
DATABASES = {
'default': { # 主库(写)
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myapp_main',
'HOST': '10.0.1.10',
'USER': 'root',
'PASSWORD': 'xxx',
},
'replica': { # 从库(读)
'ENGINE': 'django.db.backends.mysql',
'NAME': 'myapp_main',
'HOST': '10.0.1.11',
'USER': 'ro_user',
'PASSWORD': 'yyy',
'OPTIONS': {'read_only': True}, # 仅提示,Django 不校验
}
}
再创建路由类(如 db_router.py):
- 实现
db_for_read方法,返回'replica'(对所有模型启用从库读) - 实现
db_for_write方法,返回'default' -
allow_migrate必须返回True仅对'default',否则迁移会失败
最后在 settings.py 中注册:DATABASE_ROUTERS = ['myapp.db_router.ReplicaRouter']
为什么 using('replica') 有时不生效?
手动指定 .using('replica') 只影响当前 QuerySet,但关联查询(如 select_related、prefetch_related)仍走默认库 —— 这是常见盲区。
-
Book.objects.using('replica').select_related('author')中,author查询仍发向default - 解决方法:对关联字段也显式用
using,或改用原生 SQL +connections['replica'].cursor() - 事务内所有操作强制走同一库,即使写了
.using('replica')也会被忽略
如何验证读请求真的走了从库?
不能只看日志或监控,因为 Django 日志可能混用 default 名称;最可靠的是抓包或查数据库连接来源。
- 临时在从库执行
SHOW PROCESSLIST,观察是否有来自应用服务器的新连接 - 在
db_router.py的db_for_read中加print(f"READ → {db}"),配合请求触发确认 - 注意:Django Admin 默认全部走
default,需重写get_queryset手动指定.using('replica')
主从延迟导致读不到刚写入的数据怎么办?
这是读写分离的固有缺陷,Django 层无法自动解决。业务上必须做取舍:
- 强一致性场景(如用户注册后立即跳转个人页),读操作显式走
default:User.objects.using('default').get(id=123) - 避免在同一个视图里混用
using('replica')和using('default'),容易因事务隔离级别引发不可预期行为 - 不要依赖
save()后立刻refresh_from_db(),它默认仍走default,需写成obj.refresh_from_db(using='default')
真正难处理的是跨请求的一致性 —— 比如 A 请求写完,B 请求(不同进程)立刻读,此时只能靠应用层加缓存兜底或主动等待从库同步位点,Django 本身不提供这类机制。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











