当单库压力过大时,常见的两条路是:

  • 分库:按业务拆库,降低单库负载。
  • 读写分离:写走主库、读走从库。

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"

同理给 blogusers 配置 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