Django + Doris CRC32 分表实战:问题汇总与解决方案

1小时前学习2

一次从零到一的 Django 分表实践,记录踩过的所有坑

前言

最近在做一个零售户管理系统,需要处理十亿级的数据。考虑到 Doris 的强大分析能力,使用 Doris 作为数据库,并基于 CRC32 算法实现自动分表。过程并不顺利,遇到了各种各样的问题。记录这些问题及解决方案,希望能帮助到有类似需求的开发者。

问题1:Doris 数据库连接失败

错误信息:

django.db.utils.OperationalError: (2003, "Can't connect to MySQL server on 'xxx'")

问题原因:

  • Doris 使用 MySQL 协议,但端口不是默认的 3306
  • 防火墙未开放 Doris 端口
  • 未安装 PyMySQL 驱动

解决方案:

# settings.py
DATABASES = {
    'doris': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'your_database',
        'USER': 'your_user',
        'PASSWORD': 'your_password',
        'HOST': 'your_doris_host',
        'PORT': '9030',  # Doris 默认 MySQL 协议端口
        'OPTIONS': {
            'init_command': "SET sql_mode='STRICT_TRANS_TABLES'",
            'charset': 'utf8mb4',
        },
    }
}

关键点:

  • Doris 的 MySQL 协议端口通常是 9030(不是 3306)
  • 需要安装 pymysqlpip install pymysql
  • __init__.py 中添加:import pymysql; pymysql.install_as_MySQLdb()

问题2:NameError: name ‘BaseLicense’ is not defined

错误信息:

NameError: name 'BaseLicense' is not defined

问题原因:

  • router.py 中使用了 BaseLicense 但没有导入或定义
  • 模型定义顺序导致引用问题

解决方案:

方案一:在 router.py 中定义(简单但不推荐)

# router.py
class BaseLicense(models.Model):
    class Meta:
        abstract = True

方案二:分离模型和路由逻辑(推荐)

# models.py
class BaseLicense(models.Model):
    # 字段定义...
    class Meta:
        abstract = True

# router.py
from .models import BaseLicense  # 正确导入

最佳实践:

  • 将所有模型定义在 models.py
  • 路由逻辑单独放在 router.pyshard_router.py
  • 使用明确的导入路径

问题3:no such table: t_license_8

错误信息:

django.db.utils.OperationalError: no such table: t_license_8

问题原因:

  • Django 默认使用 default 数据库连接
  • default 配置的是 SQLite,但实际表在 Doris 中
  • 未指定使用 Doris 数据库

解决方案:

方案一:每次查询指定数据库(简单直接)

result = model.objects.using('doris').get(lic_no=lic_no)

方案二:配置数据库路由器(优雅)

# db_router.py
class DorisRouter:
    def db_for_read(self, model, **hints):
        if model.__name__.startswith('License_'):
            return 'doris'
        return None

# settings.py
DATABASE_ROUTERS = ['your_app.db_router.DorisRouter']

知识点:

  • using() 方法用于指定数据库
  • 数据库路由器可以实现自动化路由
  • 记得在 settings 中注册路由器

问题4:Unknown column ‘id’ in ‘t_license_8’

错误信息:

django.db.utils.OperationalError: (1054, "errCode = 2, detailMessage = Unknown column 'id' in 't_license_8'")

问题原因:

  • Django 模型默认会自动添加一个名为 id 的自增主键字段
  • Doris 表使用 retailer_uuid 作为主键,没有 id 字段

解决方案:

class BaseLicense(models.Model):
    # ✅ 必须明确指定主键字段
    retailer_uuid = models.CharField(
        max_length=96, 
        primary_key=True,  # 关键:指定为主键
        verbose_name='零售户UUID'
    )
    lic_no = models.CharField(max_length=60, null=True)
    # ... 其他字段

    class Meta:
        abstract = True
        managed = False  # 禁止 Django 管理表结构

关键点:

  • 必须用 primary_key=True 标记主键字段
  • 设置 managed = False 避免 Django 尝试创建/修改表
  • abstract = True 让基类不创建实际表

问题5:Object of type License_t_license_8 is not JSON serializable

错误信息:

TypeError: Object of type License_t_license_8 is not JSON serializable

问题原因:

  • Django ORM 返回的是模型对象,不是字典
  • JSON 序列化器无法处理 Django 模型实例

解决方案对比:

方案 代码 优点 缺点
使用 values() model.objects.filter().values().first() 直接返回字典,性能好 返回的是字典,不是模型实例
使用 model_to_dict model_to_dict(obj) 可转换已有模型实例 需要先获取实例
手动构建字典 {'field': obj.field} 完全可控 代码冗长
使用 Django 序列化器 serializers.serialize('json', queryset) 功能完整 返回格式特殊

推荐方案:

# 方案一:直接使用 values()(推荐)
@router.get("/license")
def get_license(request, lic_no: str):
    model = get_model_for_license(lic_no)
    result = model.objects.using('doris').filter(
        lic_no=lic_no
    ).values().first()  # 直接返回字典
    return result

# 方案二:使用 model_to_dict
from django.forms.models import model_to_dict

@router.get("/license")
def get_license(request, lic_no: str):
    model = get_model_for_license(lic_no)
    obj = model.objects.using('doris').get(lic_no=lic_no)
    return model_to_dict(obj)  # 转换为字典

扫描二维码推送至手机访问。

版权声明:本文由星光下的赶路人发布,如需转载请注明出处。

本文链接:https://forstyle.cc/zblog/post/115.html

分享给朋友: