分库分表需业务层路由与db层隔离,手动根据分片键(如user_id)计算目标库表并为每库配独立连接池;django须预配置分片库alias并绕过orm用原生sql;跨片查询需应用层聚合,禁用limit分页和跨库union。

分库分表不是ORM能自动解决的问题
Python Web应用(比如用Flask或Django)本身不提供分库分表能力,SQLAlchemy 和 Django ORM 都只面向单库抽象。强行让ORM“感知”分片逻辑,会导致查询路由错乱、事务失效、JOIN不可用。真正可行的路径是:在业务层做路由,在DB层做隔离,把分片逻辑收口到数据访问层。
手动路由 + 连接池分离是最可控的做法
核心是两点:一是根据分片键(如 user_id)算出目标库和表名;二是为每个物理库维护独立的连接池。不要试图复用一个 Engine 或共用一个 Pool —— 连接池混用会引发路由错乱和连接泄漏。
常见错误现象:OperationalError: (2006, "MySQL server has gone away"),往往是因为跨库连接被误复用,或超时后未正确重连。
- 用哈希或范围法决定分片:例如
shard_id = user_id % 4→ 映射到db_user_0~db_user_3 - 表名也参与分片:如
f"user_log_{user_id % 16}",避免单表过大 - 每个库单独初始化
create_engine(..., pool_pre_ping=True),启用健康检查 - 封装一个
get_shard_engine(user_id: int) -> Engine函数,内部查表映射+缓存
Django里绕过ORM直接执行分片SQL
Django的 using 参数只支持多数据库路由,不支持动态库名。想按 order_id 路由到不同库,必须跳过 Model.objects,改用原生连接:
安全 Python 代码沙箱:在隔离的沙箱中执行任意 Python 代码,预装 requests、NumPy、pandas 库。返回 stdout、stderr。
from django.db import connections
engine = connections[get_db_alias(order_id)]
with engine.cursor() as cursor:
cursor.execute("INSERT INTO order_2024 (id, amount) VALUES (%s, %s)", [order_id, 99.5])
注意:connections 是Django管理的连接池,但它的 alias 必须在 settings.DATABASES 中静态声明。所以你需要预先配好所有分片库(如 db_shard_0, db_shard_1…),再通过函数映射 order_id → alias。别在运行时动态注册新alias,Django不支持。
跨分片聚合查询只能靠应用层合并
没有分布式查询引擎(如Vitess、MyCat)时,SELECT COUNT(*) FROM user WHERE created_at > '2024-01-01' 这类语句必须拆成N条,分别发往各分片,再在Python里 sum() 结果。性能瓶颈不在SQL本身,而在网络往返和Python聚合开销。
容易被忽略的点:
- 分页不能用
LIMIT offset, size:各分片返回的“第10–20条”不是全局有序的,需取回全部再排序切片 - 时间字段务必统一用UTC,否则跨库聚合时区偏差会导致漏数
- 避免
SELECT * FROM shard_0.user UNION ALL SELECT * FROM shard_1.user—— MySQL不支持跨实例UNION
真要高频跨片统计,不如定期把各分片结果写入一个汇总库,查的时候只读汇总表。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!










