Python中Django数据库设计:大表拆小表是否有必要?

基础介绍:

  1. 主表 package 每天新增的数据量可能达到 10w 条, 预计生命周期 3 年吧(1 亿条), 不准备水平拆分
  2. 有一个 condition 表, package 根据基础信息, 在 condition 表中找到合适的 condition, 保存其外键, 留作后续统计使用
  3. django 项目

主要问题: package 的基础信息, 有 3 组: 预估信息, 校验信息, 最终信息, 三组结构基本一致 如果保存 condition 的外键, 根据三组信息, 会保存三套外键

在考虑是否需要使用 django 的 OneToOneField 进行 package 表的拆分 将大表拆分成 两张 或 三张 小表

优越的地方是 结构清晰, 主表大小降低了 但不确定的是:

  1. 是否有拆分的必要
  2. 使用 OneToOneField, 是不是一个相对比较标准的方式
  3. 除了删除不同步(但是业务并不需要删除), OneToOne 字段是否有其他问题

谢谢!


Python中Django数据库设计:大表拆小表是否有必要?

6 回复

我觉得没这个必要,如果列很多,直接指定要返回的字段就好,不过实在要拆用 OneToOneField 是挺好的,删除的问题指定好 on_delete 就行了,我记默认就是 cascade


大表拆小表(垂直分表)在Django中是否有必要,得看具体场景。

核心原则:

  • 如果表字段很多(比如30+),但每次查询只用到其中一小部分,拆表能减少I/O,提升查询速度。
  • 如果部分字段更新频繁,而其他字段几乎不变,拆开可以减少锁竞争和写放大。
  • 如果某些字段是大文本(如JSON、Text),拆出去能避免它们拖慢主表查询。

Django实现方式:

  1. 一对一关系(OneToOneField)
    把常用字段放主表,低频/大字段放副表,用OneToOneField关联。

    class UserProfile(models.Model):
        user = models.OneToOneField(User, on_delete=models.CASCADE, primary_key=True)
        avatar = models.ImageField()
        bio = models.TextField()
    

    查询时用select_related避免N+1问题。

  2. 多表继承(Multi-table Inheritance)
    Django的模型继承会自动创建一对一关联,但会多一层JOIN,慎用。

什么时候不需要拆?

  • 字段少(<20),且经常需要同时查询所有字段。
  • 数据量不大(比如百万级以内),数据库缓存命中率高。
  • 业务逻辑复杂,拆表会导致代码冗余和JOIN复杂度上升。

一句话建议: 按字段访问频率和大小拆分,避免过度设计。

1 :关于是否需要拆分,需要确认的是, django 在做数据查询时, ORM 是否会把所有数据都带出来(也就是 Select * 和 Selece 特定字段的区别)。还有一个情况就是字段的存储的大小,如果都是小字段,其实没有太大的拆分必要。如果存储的字段都是大字段,我觉得还是有拆分的必要的。
2 :建议还是用 OneToOne 的方式吧,这样可以保持一个数据库结构的完整性
3 :其他问题,暂时未知。

ps :如果 LZ 不准备做水平拆分,目前只考虑垂直拆分。只需要从存储的字段大小以及 ORM 的性能上来考虑。如有不正确的地方,请斧正。


感谢回复, 主要是至少三组数据, 每组数据有 4~5 个字段, 其中有 1~2 个是外键
如果全部存一张表, 那么担心
1. 外键太多, 一张表有十几个, 担心不太好. 个人对数据库设计, 并没有太深刻的理解, 只是喜欢一般一张表 20 个字段左右, 这样理解, 记忆什么的比较方便
2 未来如果进行业务扩展, 因为只有一张表, 那么担心这张表会过大(目前 3 组, 全部 30 个字段左右了, 扩展 2 组数据, 会有四十多个字段, 感觉表很冗余了)
最终, 选择的方案是拆分成两张表, 原则是根据业务的主次进行拆分, 主要业务放主表, 然后将辅助性的数据(相对不常用)全部放到扩展表中, 后续如果进行扩展, 也按照这个原则, 将字段添加到对应的表中.
没有根据数据逻辑, 因为根据数据逻辑, 拆分的不止两张表, 那样有点太多, 所以折中使用业务逻辑拆分.

想做水平拆分, 但是使用 django+mysql 不太会, 后面准备使用的方案是进行数据归档, 当数据量增长到一定程度, 将历史数据从原表中 copy 到另一张表(或者数据库), 再从原表中删除(以当前的技术水平, 这个方案可行性最高了,见笑了)
orm 默认不会带出所有的外键对象的数据, 如果需要, 指定 select_related(‘foreignkey’)即可, 相对来说, django orm 是真的强大+易用的

像你这种情况,不应该通过 onetotone 拆成多张表,因为你的 package 表的数据太多,一旦主表通过外建来 join 子表,开销非常大。按照你的情况,你应该就保持一张 package 表,然后通过 proxy model ( https://docs.djangoproject.com/en/1.10/topics/db/models/#proxy-models )来定义几种子类

十分感谢(还很细心的提供了文档链接)

回到顶部