当前位置: 首页 > news >正文

django mysql 分表_Django数据库分表

Django ORM没有提供默认的分表功能,给访问分表的数据库带来的不变。那么Django分表怎么实现呢?

分析Django ORM

在实现具体的方案之前,我们先看看Django ORM是如何访问数据表的。

在Django中,数据库访问的逻辑基本上是在Queryset中完成的。假设我们有如下一个User Model:

class User(models.Model):

user_id = models.IntegerField()

user_name = models.CharField(max_length=256)

password = models.CharField(max_length=256)

class Meta:

db_table = 'user'

当根据user_id查询用户数据时,比如:User.objects.filter(user_id=10).first()。

其中的objects就是models.Manager,我们看看Manager的源码:

class Manager(BaseManager.from_queryset(QuerySet)):

pass

from_queryset的源码如下

@classmethod

def from_queryset(cls, queryset_class, class_name=None):

if class_name is None:

class_name = '%sFrom%s' % (cls.__name__, queryset_class.__name__)

return type(class_name, (cls,), {

'_queryset_class': queryset_class,

**cls._get_queryset_methods(queryset_class),

})

这里可以看出Manager是对QuerySet的一个包装。QuerySet是最终要转换为SQL的一个中间层,这其实就是ORM的核心功能,把Model操作转换为SQL语句的部分。

所以当我们调用User.objects()的时候,就已经决定了要访问的数据表了。

实际访问哪个表是由models.Model中的内部类class Meta中的db_table字段决定的。

简单方案-穷举Model类分表

有了上面背景知识,假如我们要根据user_id给user表分三个表。在Django models里如何支持呢?

最直接的思路就是声明三个User Model,然后在Meta类中,指定不同的db_table。在查询的时候,根据user_id返回具体的Model。

实现方法如下:

class User(models.Model):

user_id = models.IntegerField(primary_key=True)

user_name = models.CharField(max_length=256)

password = models.CharField(max_length=256)

class User0(User):

class Meta:

db_table = 'user_0'

class User1(User):

class Meta:

db_table = 'user_1'

class User2(User):

class Meta:

db_table = 'user_2'

user_model_map = {

0: User0,

1: User1,

2: User2,

}

table_num = 3

def get_user_db_model(user_id):

key = user_id % table_num

return user_model_map[key]

然后,在实际查询时,先获取user model,再根据user_id查询数据。

def get_user_by_id(user_id):

db_model = models.get_user_db_model(user_id)

user = db_model.objects.get(user_id=user_id)

return model_to_dict(user)

这样就可以支持访问分表的数据库了。

但是,聪明的大家都能看出来,要是user分100个表,甚至1000个表的时候怎么办呢?难道定义1000个User类?

Python这么牛逼的语言,怎么可以这么糟蹋呢?下面继续探究一下更好的方法吧。

改进方案-动态创建Model类

既然不通过复制粘贴来定义1000个类,那么就需要动态去创建这1000个类。

学过Java的同学都知道反射,Java的框架和ORM必然是用到反射的,需要动态处理一些类信息。

而Python作为一门动态语言,天生就支持Java 反射里各种黑科技的功能,对,我们不需要反射,原生Python已经自带了。

下面看看在Django里怎样去动态创建我们需要的1000个User类。

from django.db import models

TABLE_NUM = 3

class User(models.Model):

@classmethod

def get_user_db_model(cls, user_id=None):

suffix = user_id % TABLE_NUM

table_name = 'user_%s' % suffix

if table_name in cls._user_model_dict:

return cls._user_model_dict[table_name]

class Meta:

db_table = table_name

attrs = {

'__module__': cls.__module__,

'Meta': Meta,

}

user_db_model = type(str('User_%s' % suffix), (cls,), attrs)

cls._user_model_dict[table_name] = user_db_model

return user_db_model

_user_model_dict = {}

user_id = models.IntegerField()

user_name = models.CharField(max_length=256)

password = models.CharField(max_length=256)

class Meta:

abstract = True

嗯,看起来不错,我们已经不需要用复制粘贴来定义1000个类了,而且实现同样的功能代码短了不少。

依然可通过如下的方式获取数据:

db_model = User.get_user_db_model(user_id)

user = db_model.objects().get(user_id=user_id)

但是这里还有优化的空间,因为每次查询我们都创建了一个User Model对象,岂不是很浪费资源,那这些对象可以复用嘛?

改进-动态创建+本地缓存

怎么复用呢?自然是用字典把已经创建过的Model缓存下来,虽然这会消耗一部分本地内存,但为了提高性能还是值得的。

from django.db import models

TABLE_NUM = 3

class User(models.Model):

@classmethod

def get_user_db_model(cls, user_id=None):

suffix = user_id % TABLE_NUM

table_name = 'user_%s' % suffix

if table_name in cls._user_model_dict:

return cls._user_model_dict[table_name]

class Meta:

db_table = table_name

attrs = {

'__module__': cls.__module__,

'Meta': Meta,

}

user_db_model = type(str('User%s' % suffix), (cls,), attrs)

cls._user_model_dict[table_name] = user_db_model

return user_db_model

_user_model_dict = {}

user_id = models.IntegerField(primary_key=True)

user_name = models.CharField(max_length=256)

password = models.CharField(max_length=256)

class Meta:

abstract = True

我们定义了一个私有变量_user_model_dict,以table_name作为key,model对象作为值,把已经创建的Model类都缓存下来。

但到了这里还是有点麻烦。

这里获取db_model,在访问数据库时,还是没有直接objects.get()来的方便。

db_model = User.get_user_db_model(user_id)

user = db_model.objects().get(user_id=user_id)

VS

user = User.objects().get(user_id=user_id)我们的分表只支持了User一个表,其他表分表还得再写一个get_xx_db_model,可不可以做的更通用些呢?

改进-Hack Model类的创建

针对上面的问题,可以hack Django Model的创建,在动态创建生成Model类时修改一些属性,把分表需要的信息填充进去。

我们可以创建一个基类来实现动态创建的功能,不仅支持User Model分表,还能方便的让X Model, Y Model通过继承也支持分表。

from django.db import models

class Object:

def __init__(self, **kwargs):

self.__dict__.update(kwargs)

def _model_new(cls, *args, **kwargs):

return cls(*args, **kwargs)

class ShardModel(object):

"""ShardModel support table horizontal partition."""

_shard_db_models = {}

def __new__(cls, *args, **kwargs):

shard_key = kwargs.pop('shard_key', 0) % cls.Config.table_num

model_name = cls.__name__

model_name += '_%s' % shard_key

model_class = cls._shard_db_models.get(model_name)

if model_class is not None:

return model_class

# Deep copy attrs

attrs = dict()

attrs.update(cls.__dict__)

if 'objects' in attrs:

attrs['objects'] = attrs['objects'].__class__()

# Set table name with shard_key

meta = Object(**cls.Meta.__dict__)

meta.db_table = meta.db_table % shard_key

attrs['Meta'] = meta

attrs['new'] = classmethod(_model_new)

# Create model class dynamically

model_class = type(model_name, tuple([models.Model] + list(cls.__bases__[1:])), attrs)

cls._shard_db_models[model_name] = model_class

return model_class

class User(ShardModel):

user_id = models.IntegerField()

user_name = models.CharField(max_length=256)

password = models.CharField(max_length=256)

class Config:

table_num = 3

class Meta:

app_label = 'default'

db_table = 'user_%s'

通过继承ShardModel,User类就具备的自动分表的功能。

在views中可通过如下方法获取user info:

def get_user_info(request):

user_id = int(request.GET.get('user_id'))

user = models.User(shard_key=user_id).objects.get(user_id=user_id)

return HttpResponse(json.dumps(model_to_dict(user)))

配置好Django urls,在浏览器访问http://127.0.0.1:8000/user/get?user_id=32,得到下面的结果:

{

"user_name": "test32",

"password": "test32",

"user_id": 32,

"id": 1

}

nice, it works!

如果我们有一个Book表也需要分表,这样只需要简单的继承就行了。

class Book(ShardModel):

book_id = models.IntegerField()

book_name = models.CharField(max_length=256)

book_author = models.CharField(max_length=256)

这样我们就通过层层深入,不断改进我们的方案,得到了一个比较优雅实现Django分表的方法。

本文由 络壳 原创或整理,转载请注明出处

相关文章:

  • php aws_Amazon S3 客户端加密与 AWS SDK for PHP 版本 3 - 适用于 PHP 的 AWS 开发工具包...
  • Php公钥加密data是空,实用的PHP带公钥加密类分享(每次加密结果都不一样哦)
  • java做节奏大师,《节奏大师》高手必修之路 亲测攻略_iOS游戏频道_97973手游网
  • 注册php tp5,thinkphp5 开发会员注册与登录功能
  • php xml序列化,深入理解:XML与对象的序列化与反序列化
  • matlab ceil,Matlab中的取整函数fix, floor, ceil与round
  • php 创建mssql 表,在php表中显示MSSQL选择数据
  • matlab 热图,基于表格数据创建热图
  • getlocation.php,关于微信小程序 location API接口的解析
  • php 调用未定义方法,关于php:Laravel调用未定义的方法create()和save()
  • eclipse java 源代码,ECLIPSE 调试java 源码
  • excel按条件查询mysql,excel多个表格满足条件的数据库-excel表格满足多条件的数据汇总(vlookup?)...
  • php多张图片制作成视频教程,PS如何将一张图片做成多张图片叠加效果
  • matlab查看hdf5数据,Matlab 操作 HDF5文件
  • 蓝桥杯java c组真题解析,蓝桥杯C语言C组校内赛题目解析
  • 【391天】每日项目总结系列128(2018.03.03)
  • 【译】理解JavaScript:new 关键字
  • 2017-09-12 前端日报
  • angular组件开发
  • CSS 提示工具(Tooltip)
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • Java 多线程编程之:notify 和 wait 用法
  • java2019面试题北京
  • JS进阶 - JS 、JS-Web-API与DOM、BOM
  • Laravel 实践之路: 数据库迁移与数据填充
  • PV统计优化设计
  • React as a UI Runtime(五、列表)
  • Sass Day-01
  • spring boot 整合mybatis 无法输出sql的问题
  • 关于 Cirru Editor 存储格式
  • 漂亮刷新控件-iOS
  • 数据结构java版之冒泡排序及优化
  • nb
  • 06-01 点餐小程序前台界面搭建
  • media数据库操作,可以进行增删改查,实现回收站,隐私照片功能 SharedPreferences存储地址:
  • 曜石科技宣布获得千万级天使轮投资,全方面布局电竞产业链 ...
  • ​​快速排序(四)——挖坑法,前后指针法与非递归
  • ​Linux Ubuntu环境下使用docker构建spark运行环境(超级详细)
  • #HarmonyOS:基础语法
  • #vue3 实现前端下载excel文件模板功能
  • $(selector).each()和$.each()的区别
  • (1)(1.13) SiK无线电高级配置(五)
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (echarts)echarts使用时重新加载数据之前的数据存留在图上的问题
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (状压dp)uva 10817 Headmaster's Headache
  • *Algs4-1.5.25随机网格的倍率测试-(未读懂题)
  • .NET Remoting Basic(10)-创建不同宿主的客户端与服务器端
  • .NET/C# 利用 Walterlv.WeakEvents 高性能地定义和使用弱事件
  • .net开发时的诡异问题,button的onclick事件无效
  • .net快速开发框架源码分享
  • .NET与 java通用的3DES加密解密方法
  • @ 代码随想录算法训练营第8周(C语言)|Day53(动态规划)
  • @requestBody写与不写的情况
  • [17]JAVAEE-HTTP协议