发表时间:2020-08-25
对网站的数据库作读写分离(Read/Write Splitting)可以提高性能,在Django中对此提供了支持,下面我们来简单看一下。注意,还需要运维人员作数据库的读写分离和数据同步 -DBA。
我们知道在Django项目的settings中,可以配置数据库,除了默认的数据库,我在下面又加了一个db2。因为是演示,我这里用的是MySQL 。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'AAAA',
'HOST': 'XXXXX',
'PORT': 3306,
'USER': 'root',
'PASSWORD': '1qaz!QAZ',
'CHARSET': 'utf8',
'TIME_ZONE': 'Asia/Shanghai',
},
'slave1': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'AAAA_slave1',
'HOST': 'XXXXX',
'PORT': 3306,
'USER': 'root',
'PASSWORD': '1qaz!QAZ',
'CHARSET': 'utf8',
'TIME_ZONE': 'Asia/Shanghai',
},
}
# 数据库路由配置
DATABASE_ROUTERS = [
# 'common.db_routers.MultiDbRouter', # 多数据库配置
'common.db_routers.MasterSlaveRouter', # 主从数据库路由配置
]
其实第二步迁移默认有参数python manage.py migrate --database default ,在默认数据库上创建表。因此完成以上迁移后,执行python manage.py --database slave1,再迁移一次,就可以在slave1上创建相同的表。
手动读写分离
在使用ORM 调用数据库时,通过.using(db_name)来手动指定要使用的数据库
通过配置数据库路由,来自动实现,这样就不需要每次读写都手动指定数据库了。数据库路由中提供了四个方法。这里这里主要用其中的两个:def db_for_read()决定读操作的数据库,def db_for_write()决定写操作的数据库。
定义Router类
新建db_route.py脚本,定义Router类:
import random
# class MultiDbRouter:
# """多数据库路由"""
#
# @staticmethod
# def db_for_read(model, **hints):
# if model._meta.app_label == 'ar_map':
# return 'slave1'
# return 'default'
#
# @staticmethod
# def db_for_write(model, **hints):
# if model._meta.app_label == 'ar_map':
# return 'slave1'
# return 'default'
#
# @staticmethod
# def allow_relation(obj1, obj2, **hints):
# return None
#
# @staticmethod
# def allow_migrate(db, app_label, model_name=None, **hints):
# return True
class MasterSlaveRouter:
"""主从复制路由"""
@staticmethod
def db_for_read(model, **hints):
'''指定xxx_app 读数据时,使用指定setting配置的数据库xxx'''
if model._meta.app_label == 'xxx_app':
return 'xxx'
return random.choice(('slave1', ))
@staticmethod
def db_for_write(model, **hints):
'''指定xxx_app 写的时候,使用指定setting配置的数据库xxx'''
if model._meta.app_label == 'xxx_app':
return 'xxx'
return 'default'
@staticmethod
def allow_relation(obj1, obj2, **hints):
return None
@staticmethod
def allow_migrate(db, app_label, model_name=None, **hints):
''' python manage.py migrate --database=slave1
django默认迁移 default 数据库
'''
return True
'''
稍微追溯一下源码, 当需要一个db时, 拿数据库的manager举例, 会通过django.db的router去获取
而router就是django.db.utils中定义的一个类, 它使用了setttings.py中DATABASE_ROUTERS配置.
/home/liwuang/.pyenv/versions/3.6.5/lib/python3.6/site-packages/django/db/utils.py
'''
配置Router
settings.py中已经指定DATABASE_ROUTERS
DATABASE_ROUTERS = [
# 'common.db_routers.MultiDbRouter', # 多数据库配置
'common.db_routers.MasterSlaveRouter', # 主从数据库路由配置
]
可以指定多个数据库路由,比如对于读操作,Django将会循环所有路由中的db_for_read()方法,直到其中一个有返回值,然后使用这个数据库进行当前操作。
网站的读的性能通常更重要,因此,可以多配置几个数据库,并在读取时,随机选取,比如:
class Router:
def db_for_read(self, model, **hints):
"""
读取时随机选择一个数据库
"""
import random
return random.choice(['db2', 'db3', 'db4'])
def db_for_write(self, model, **hints):
"""
写入时选择主库
"""
return 'default'
在大型web项目中,常常会创建多个app来处理不同的业务,如果希望实现app之间的数据库分离,比如app01走数据库db1,app02走数据库
class Router:
def db_for_read(self, model, **hints):
if model._meta.app_label == 'app01':
return 'db1'
if model._meta.app_label == 'app02':
return 'db2'
def db_for_write(self, model, **hints):
if model._meta.app_label == 'app01':
return 'db1'
if model._meta.app_label == 'app02':
return 'db2'
参考地址:https://www.pythonf.cn/read/140551