Django数据库分库+读写分离
为了解决单个数据库的性能问题,除了使用性能更好的硬件之外, 另外一个思路就是将一个数据库切分成多个部分放到不同的数据库上,从而缓解单一数据库的性能问题。
Django ORM
提供了默认的分库功能,只需要在settings.py
文件中添加相应的数据库配置。
例如我们在Django
项目有users
, book
, blog
三个app,
对应user
,book
,blog
三个DB,下面就探究一下如何在Django实现这三个库的分库?
如何配置分库
配置DB地址
在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 = {
# example:
# 'app_name':'database_name',
'blog': 'db_blog',
'book': 'db_book',
'users': 'default',
}
这里我们定义了三个库和地址,default
, db_blog
和db_book
;以及app name到db name的映射。
定义Model
对应的,在book app的models中,我们定义如下Book类。
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
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 app的models中,我们定义如下Blog类。
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models
# Create your models here.
class Blog(models.Model):
blog_id = models.IntegerField(primary_key=True)
title = models.CharField(max_length=256)
content = models.TextField()
class Meta:
app_label = 'blog'
db_table = 'blog_tab'
类似的,在users app中定义User类。
执行migrate
migrate
命令在同步变更时,每次操作一个数据库,默认操作default
数据库。通过--database
选项,可以指定操作的数据库。
python manage.py migrate
python manage.py migrate --database=db_book
python manage.py migrate --database=db_blog
通过上面的命令,把models里定义的信息,同步到数据库。
如何访问分库
通过前面几步,就完成了Django数据库分库的配置。那配置完了怎样使用呢? 有两种方法来使用分库:
using()
方法- 指定
Database Router
using()
方法
在没有配置路由前,我们需要通过using
方法指定需要访问的数据库,代码入侵比较强。示例:
- 指定保存的数据库
b = Blog(content='test')
b.save(using='db_blog')
- 指定读取的数据库
Book.objects.using('db_book').all()
配置DB路由
配置Database routers
需要新建一个db_router.py
文件,添加自定义的数据库路由类DbRouter
。
这个类里需要实现四个方法:db_for_read
、db_for_write
、allow_relation
以及allow_migrate
来定义路由规则,
我们可以根据app_label
来指定访问的DB。
from django.conf import settings
DATABASE_MAPPING = settings.DATABASE_APPS_MAPPING
class DbRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def db_for_write(self, model, **hints):
if model._meta.app_label in DATABASE_MAPPING:
return DATABASE_MAPPING[model._meta.app_label]
return None
def allow_relation(self, obj1, obj2, **hints):
pass
def allow_migrate(self, db, app_label, model=None, **hints):
pass
然后,在settings.py
文件添加数据库路由的配置DATABASE_ROUTERS
,如下:
DATABASE_ROUTERS = ['partition.db_router.DbRouter'] # partition是项目名
配置在DATABASE_ROUTERS
中的路由规则才会生效,能被django.db.router
使用。
我们可以指定多个数据库路由规则,比如对于读操作,Django将会循环所有路由中的
db_for_read()
方法,直到其中一个有返回值,然后使用这个数据库进行当前操作。
按完成了上述的路由配置后,Django ORM
会根据路由规则来选择访问的DB。
按照上面DbRouter
的配置,user会访问default库,book访问db_book库,blog访问db_blog库。
例如:Book.objects.all()
会自动访问db_book
数据库,无需再通过using
来指定数据库。
如何配置读写分离
数据库读写分离
是只是数据库分库的一种特殊情况,在现有的基础上,我们只需要调整下配置就可以实现读写分离了。DbRouter
示例:
主库写从库读
class DbRouter:
def db_for_read(self, model, **hints):
"""
读取时随机选择一个从库
"""
import random
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'