Django多数据库配置与分库读写分离实战指南
本文详解Django中多数据库的配置与路由机制,涵盖垂直与水平分库方案。通过自定义DatabaseRouter实现读写分离及一主多从架构,并对比自动路由与手动路由的性能差异,提供查询、保存及删除操作的具体代码示例,帮助开发者在小规模项目中高效管理多数据库环境。
定义及路由机制
定义
在Django项目的settings配置文件中,DATABASES是一个字典结构,用于定义项目所需的所有数据库连接信息。例如,以下配置示例中定义了两个独立的数据库实例。
DATABASES = {
'default': {
'NAME': 'app_data',
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'USER': 'postgres_user',
'PASSWORD': 's3krit'
},
'user1': {
'NAME': 'user1_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'priv4te'
}
'user2': {
'NAME': 'user2_data',
'ENGINE': 'django.db.backends.mysql',
'USER': 'mysql_user',
'PASSWORD': 'priv4te'
}
}
那么在实际运行中,Django何时调用default数据库,何时调用user1数据库呢?这取决于后续配置的路由规则。
路由注册
class User1Router(object): """ A router to control all database operations on models in the auth application. """ def db_for_read(self, model, **hints): """ Attempts to read auth models go to auth_db. """ if model._meta.app_label == 'auth': return 'user1' return None def db_for_write(self, model, **hints): """ Attempts to write auth models go to auth_db. """ if model._meta.app_label == 'auth': return 'user1' return None def allow_relation(self, obj1, obj2, **hints): """ Allow relations if a model in the auth app is involved. """ if obj1._meta.app_label == 'auth' or \ obj2._meta.app_label == 'auth': return True return None def allow_syncdb(self, db, model): """ Make sure the auth app only appears in the 'auth_db' database. """ if db == 'auth_db': return model._meta.app_label == 'auth' elif model._meta.app_label == 'user1': return False return None class User2Router(object): """ A router to control all database operations on models in the auth application. """ def db_for_read(self, model, **hints): """ Attempts to read auth models go to auth_db. """ if model._meta.app_label == 'auth2': return 'user2' return None def db_for_write(self, model, **hints): """ Attempts to write auth models go to auth_db. """ if model._meta.app_label == 'auth2': return 'user2' return None def allow_relation(self, obj1, obj2, **hints): """ Allow relations if a model in the auth app is involved. """ if obj1._meta.app_label == 'auth' or \ obj2._meta.app_label == 'auth': return True return None def allow_syncdb(self, db, model): """ Make sure the auth app only appears in the 'auth_db' database. """ if db == 'auth_db': return model._meta.app_label == 'auth2' elif model._meta.app_label == 'user2': return False return None
User1Router的路由逻辑设定为:当模型所属的app_label为auth时,数据读写操作指向user1数据库;其他情况则遵循默认或其他路由规则。User2Router的逻辑与此类似,用于处理不同的业务模块。
如何注册路由
仅定义路由类无法生效,必须将其注册到Django框架中。具体操作是在settings.py文件中添加DATABASE_ROUTERS配置项。
DATABASE_ROUTERS = ['path.to.User1Router' , 'path.to.User2Router']
其中path.to表示User1Router类的完整Python包路径。因此,User1Router类无需必须定义在settings.py中,可以放置在项目任意可用的模块目录下。
路由机制
Django内部是如何选择具体路由器的呢?其核心机制如下:
1. Django会按照DATABASE_ROUTERS列表中注册的顺序,依次轮询每个路由器。首先检查User1Router是否返回非空字符串(即具体的数据库名称)。如果返回了非空值,则直接使用该数据库;如果返回None,则继续检查下一个路由器。
2. 接着验证User2Router,若其返回非空字符串,则使用该数据库;若仍返回None,则最终回退到default数据库。
3. 由此可见,路由器的注册顺序直接影响结果。注册在列表前面的路由器具有更高的优先级,会被优先匹配和使用。
自动路由和手动路由
上述通过DATABASE_ROBERS配置的路由方式称为自动路由。其含义是Django会在每次数据库操作时自动轮询已注册的路由器列表,根据模型属性自动决定数据存储在哪个数据库,开发者在业务代码中无需显式指定。
手动路由则允许开发者在代码中显式指定某个Model实例应保存或读取自哪个数据库。
此外,手动路由在性能上具有一定优势。若定义了多个数据库且使用自动路由,每次保存或读取Model时都需要遍历整个路由列表,效率较低。如果程序逻辑清晰,明确知道当前代码应连接哪个数据库,显式指定方式能避免轮询开销,效率更高。
手动路由
查询
在执行查询时,使用QuerySet的using()方法,参数指定目标数据库名称。
User.objects.using('user1').all()
保存或者更新
在调用Model的save()方法时,传入using参数,值为目标数据库名称。
>>> my_object.save(using='user1')
删除
在调用Model的delete()方法时,同样传入using参数指定目标数据库。
>>> user_obj.delete(using='user1')
分库技术
下面简要介绍两种常见的分库思路。
垂直分库
垂直分库的核心思想是一个App对应一个独立的数据库。上述自动路由的例子即为垂直分库的典型场景:auth1模块的数据存入user1数据库,auth2模块的数据存入user2数据库。当然,垂直分库也可以结合手动路由来实现。
水平分库
对于水平分库,建议采用手动路由。因为不同Model的分库规则可能各异,使用自动路由实现复杂且可能影响性能。而手动路由允许每个Model根据自定义规则灵活选择数据库,更加高效。
补充知识:Django实现数据库读写分离、一主多从、分库
读写分离
在工程实践中,MySQL读写分离是常见需求。在Django中实现读写分离只需几步简单配置。
首先,配置读库和写库的连接信息。
在django项目的settings.py文件中,分别配置主库(写库)和从库(读库)的连接参数。
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'WIPS',
'USER': 'mysql',
'PASSWORD': '360tianxun#^)Sec',
'HOST': '',
'PORT': '',
},
'slave': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'TEST',
'USER': 'mysql',
'PASSWORD': '360tianxun#^)Sec',
'HOST': '',
'PORT': '',
},
}
接下来,需要创建数据库的路由分发类。
可以在appname/utils目录下创建一个db_router.py文件,并在其中定义DBRouter类。在该类中实现db_for_read和db_for_write方法,以区分读库和写库的选择逻辑。
class DBRouter(object): 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
最后,在settings.py中添加路由配置,将自定义的路由器类注册到系统中。
DATABASE_ROUTERS = ['appname.utils.db_router.DBRouter' ]
完成上述配置后,重启Django服务即可生效。
这里需要注意的是,Django仅负责应用层面的读写分离逻辑,MySQL主库与从库之间的数据同步操作仍由MySQL自身机制负责,Django不介入此过程。
一主多从
一主多从方案在实际应用中更为常见。在读写分离配置的基础上,只需修改少量代码即可实现一主多从架构。
首先,修改settings.py,增加所有从库的连接配置。
其次,修改db_router类中的db_for_read()方法。以下示例展示了随机选取读库的逻辑,实际应用中也可根据负载情况选择其他调度算法。
class DBRouter(object): def db_for_read(self, model, **hints): import random return random.choice(['slave', 'slave2', 'slave3'])
分库
当需要不同App使用不同数据库时,可以利用Model中的app_label属性来实现数据库路由。
class DBRouter(object): def db_for_read(self, model, **hints): if model._meta.app_label == 'app01': import random return random.choice(['app01_slave1', 'app01_slave2', 'app01_slave3']) if model._meta.app_label == 'app02': return "app02_slave"
按照上述步骤,即可轻松实现MySQL的读写分离、一主多从以及分库功能。但需注意,该方法仅建议用于小规模项目,大型项目需考虑更复杂的分布式方案。
以上这篇django 多数据库及分库实现方式就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持本站。
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













