当单库压力过大时,常见的两条路是:
- 分库:按业务拆库,降低单库负载。
- 读写分离:写走主库、读走从库。
Django ORM 原生支持多库,只需要配置并加路由即可。
1. 配置多数据库
在 settings.py 里配置 DATABASES:
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
},
"db_book": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db_book.sqlite3"),
},
"db_blog": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db_blog.sqlite3"),
},
}
DATABASE_APPS_MAPPING = {
"blog": "db_blog",
"book": "db_book",
"users": "default",
}
2. 模型归属(app 对应库)
示例:book app 的模型
from django.db import models
class Book(models.Model):
book_id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=256)
author = models.CharField(max_length=256)
class Meta:
app_label = "book"
db_table = "book_tab"
同理给 blog、users 配置 app_label。
3. 多库迁移
migrate 默认只操作 default,需要显式指定:
python manage.py migrate
python manage.py migrate --database=db_book
python manage.py migrate --database=db_blog
4. 访问多库
方式一:using() 手动指定
b = Blog(content="test")
b.save(using="db_blog")
Book.objects.using("db_book").all()
优点:直观;缺点:侵入性强。
方式二:配置数据库路由(推荐)
新增 db_router.py:
from django.conf import settings
DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING
class DbRouter:
def db_for_read(self, model, **hints):
return DATABASE_MAPPING.get(model._meta.app_label)
def db_for_write(self, model, **hints):
return DATABASE_MAPPING.get(model._meta.app_label)
def allow_relation(self, obj1, obj2, **hints):
return None
def allow_migrate(self, db, app_label, model_name=None, **hints):
if app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[app_label] == db
return None
在 settings.py 配置:
DATABASE_ROUTERS = ["partition.db_router.DbRouter"]
这样 Book.objects.all() 会自动路由到 db_book。
5. 读写分离
读写分离是分库的特殊情况,路由规则只需区分读与写。
主库写、从库读
import random
class DbRouter:
def db_for_read(self, model, **hints):
return random.choice(["db_slave2", "db_slave3", "db_slave4"])
def db_for_write(self, model, **hints):
return "default"
多 app 的读写分离
class DbRouter:
def db_for_read(self, model, **hints):
if model._meta.app_label == "book":
return "db_book_slave"
if model._meta.app_label == "blog":
return "db_blog_slave"
def db_for_write(self, model, **hints):
if model._meta.app_label == "book":
return "db_book_master"
if model._meta.app_label == "blog":
return "db_blog_master"
小结
- 业务拆库要配路由,否则要手工
using()。 - 读写分离只需改路由规则。
- 迁移要记得指定
--database。