Django + Doris CRC32 分表实战:问题汇总与解决方案
一次从零到一的 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)
- 需要安装
pymysql:pip 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.py或shard_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) # 转换为字典



