本文目录导读:

- 📖 目录导读
- 为什么需要读写分离?
- Django实现读写分离的常见方案
- 官方配置步骤详解(含代码)
- 路由规则实战:主库写、从库读
- 自动切换与手动切换的取舍
- 常见错误与性能陷阱
- 问答环节:你可能关心的5个问题
- 总结:到底简单不简单?
Django读写分离配置简单吗?一文带你踩坑与避坑
📖 目录导读
- 为什么需要读写分离?
- Django实现读写分离的常见方案
- 官方配置步骤详解(含代码)
- 路由规则实战:主库写、从库读
- 自动切换与手动切换的取舍
- 常见错误与性能陷阱
- 问答环节:你可能关心的5个问题
- 到底简单不简单?
为什么需要读写分离?
在高并发场景下,数据库往往是系统的瓶颈,读写分离的核心思想是:将写操作(INSERT/UPDATE/DELETE)指向主库,将读操作(SELECT)指向从库,从而分摊数据库压力,提升查询性能。
对于Django项目,默认情况下所有SQL请求都发往同一个数据库连接,当访问量上升,单库的CPU、连接数、磁盘IO都会吃紧,此时读写分离成为必要手段。
Django实现读写分离的常见方案
目前主流方案有两种:
-
方案A:使用
django-db-router或自定义DATABASE_ROUTERS
纯Django原生支持,无需额外中间件,灵活度高。 -
方案B:结合数据库中间件(ProxySQL、Mycat等)
对应用透明,但增加运维复杂度,且可能引入网络延迟。
大多数中小项目推荐方案A,你只需要配置多数据库连接,并编写一个路由类即可。
官方配置步骤详解(含代码)
1 修改settings.py,添加两个数据库配置
DATABASES = {
'default': { # 主库(写)
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mydb',
'USER': 'root',
'PASSWORD': 'master_pwd',
'HOST': '192.168.1.10',
'PORT': '3306',
},
'slave': { # 从库(读)
'ENGINE': 'django.db.backends.mysql',
'NAME': 'mydb',
'USER': 'root',
'PASSWORD': 'slave_pwd',
'HOST': '192.168.1.11',
'PORT': '3306',
}
}
2 创建路由类
新建文件utils/db_router.py:
class MasterSlaveRouter:
def db_for_read(self, model, **hints):
"""读操作自动指向从库"""
return 'slave'
def db_for_write(self, model, **hints):
"""写操作指向主库"""
return 'default'
def allow_relation(self, obj1, obj2, **hints):
"""允许跨库关系(如有必要)"""
return True
def allow_migrate(self, db, app_label, model_name=None, **hints):
"""迁移只对主库执行"""
return db == 'default'
3 注册路由
在settings.py中添加:
DATABASE_ROUTERS = ['utils.db_router.MasterSlaveRouter']
完成以上三步,Django就会自动将查询类请求(如.all(), .filter())发送到从库,将增删改发送到主库。
路由规则实战:主库写、从库读
1 默认行为
# 写操作:自动走主库 User.objects.create(name='Alice') # -> default # 读操作:自动走从库 users = User.objects.filter(is_active=True) # -> slave
2 手动指定数据库
某些业务场景(如刚写入后立即查询),你需要强制读主库:
# 强制使用主库读取最新数据
user = User.objects.using('default').get(id=1)
3 考虑延迟场景
使用select_related或prefetch_related时,务必注意:如果关联模型在写操作后立刻查询,可能因为主从延迟读到旧数据,一种解决手段是给关键操作增加重试逻辑,或使用事务强制走主库。
自动切换与手动切换的取舍
| 对比项 | 自动切换(路由类) | 手动切换(using()) |
|---|---|---|
| 代码侵入性 | 低,全局生效 | 中,需在每个查询加参数 |
| 灵活度 | 较低,统一规则 | 高,可针对不同场景定制 |
| 适用场景 | 大部分读写比例稳定 | 对一致性要求高的场景 |
| 维护成本 | 低 | 中 |
建议:默认使用自动路由,仅在强一致性处手动指定主库。
常见错误与性能陷阱
❌ 错误1:忘记迁移从库表结构
主从库表结构必须一致,如果只对主库执行migrate,从库会因表缺失报错。
解决方案:从库作为只读热备,可用manage.py migrate --database=slave手动同步,或使用复制工具。
❌ 错误2:读写分离后ORM查询依然全表扫描
路由只决定请求去哪个库,不改变SQL本身,仍需合理加索引,避免慢查询。
❌ 错误3:在写操作后的同一个请求内立即读取
推荐做法:写之后立即读,强制走主库,否则可能读到旧数据。
❌ 性能陷阱:频繁的using()调用
过度使用会破坏路由策略,导致主库压力飙升。
问答环节:你可能关心的5个问题
Q1:Django读写分离配置简单吗?
A:如果只做基础配置,只需写一个路由类+2组数据库连接,确实很简单,但如果要处理主从延迟、跨库关联、动态切换等高级场景,复杂度明显上升。
Q2:必须使用DATABASE_ROUTERS吗?
A:不是,你也可以通过using()在每个DB操作中手动指定,但那样代码冗余且易出错。
Q3:读写分离后,主库挂了怎么办?
A:Django本身无自动故障转移,建议搭配数据库中间件(如ProxySQL)或Django-tenant-schemas等三方库实现高可用。
Q4:多个从库如何轮询?
A:可从路由类返回不同从库名称,或使用random.choices随机挑选,注意要维护连接池。
Q5:对已有项目增加读写分离,改动大吗?
A:如果原项目所有操作都使用ORM,且没有硬编码数据库名,只需改动settings.py和新增路由类,几乎零侵入,但如果大量使用raw()原生SQL,则需逐个检查。
到底简单不简单?
基础场景下,Django读写分离配置非常简单,你只需要:
- 在
settings.py中定义两个数据库连接; - 编写一个
MasterSlaveRouter类; - 注册到
DATABASE_ROUTERS。
整个过程约20行代码,5分钟内可完成。
但若需应对以下场景,则难度陡增:
- 主从延迟导致数据一致性要求
- 复杂的读写策略(如部分读走从库、部分读走主库)
- 多从库负载均衡
- 迁移、监控、故障恢复
回答“Django读写分离配置简单吗?”——简单的是入门,不简单的是生产级落地,如果你的项目日均PV低于10万,单库够用,暂时不必上分离;如果已经遇到性能瓶颈,请务必规划好延迟容忍度和监控策略再动手。