thinkphp原生不支持跨库join、全局唯一主键和分布式事务;tp5.1的partition仅支持单库水平分表,不支持跨表关联查询与自定义分片逻辑;tp6无内置分库能力,需手动切换连接,事务和关联查询受限。

ThinkPHP 原生不支持跨库 JOIN、全局唯一主键、分布式事务,强行分库分表会直接卡在查询和一致性上。真要落地,必须明确接受“放弃部分 SQL 能力”这个前提。
ThinkPHP5 的 partition 方法只支持单库内水平分表
TP5.1 的 partition 是个轻量级分表工具,底层调用 getPartitionTableName,仅在单数据库中生成类似 t_chat_2023、t_chat_2024 这样的子表名。它不碰连接池、不改 PDO 实例、不路由到不同 DB 实例。
- 适用场景:日志、聊天记录等按年/月/ID 范围可预判的只读或低频写入表
- 不支持
JOIN分表与非分表(如t_user与t_chat_2024关联查)——会报Table 'db.t_chat_2024' doesn't exist或字段找不到 - 若查询条件没带分表字段(比如查所有年份的聊天),它会自动拼
UNION,但性能差、无法加索引、不能LIMIT下推 - 分表规则写死在模型里,
type只认id、year、mod、md5四种,没法扩展自定义哈希逻辑
ThinkPHP6 没有内置分库能力,得靠中间件或手动切换连接
TP6 官方文档里没有 sharding 概念,database.php 中的 connections 是静态配置,不支持运行时根据 user_id 动态选库。所谓“分库中间件”,实际是社区方案(如 think-swoole + 自定义连接管理器),不是框架原生能力。
- 常见做法:在模型基类或 Repository 层,用
$this->connection('db_' . ($uid % 4))手动指定连接标识 - 隐患:事务只能在单库内生效;
Db::transaction()无法跨库;with()关联预加载失效 - 如果用 Swoole 长连接,还要注意连接复用时的库上下文污染,比如前一个请求用了
db_2,下一个没重置就可能连错 - 迁移脚本、数据校验、备份恢复都得按库遍历,没法用 mysqldump 一键导出全量
跨表查询别指望 ORM,老实用原生 SQL 或应用层聚合
TP 的 join、union、view 全部基于单连接实例,一旦表分散在不同库或不同分表,where 条件无法下推,order by 和 limit 会失效——你拿到的是多个结果集拼起来的未排序数据。
- 正确姿势:先查主表(如用户 ID 列表),再按分片规则算出目标表/库,循环查各子集,最后在 PHP 层
array_merge+usort - 避免全量扫:不要用
SELECT * FROM t_order_*这种通配,TP 不解析通配符,只会当字面表名报错 - 时间范围查询(如查最近 3 个月订单)可以提前算出涉及哪些分表(
t_order_202602、t_order_202603、t_order_202604),再用UNION ALL拼接,但必须手写Db::raw() - 计数、分页、去重这类操作,基本要放弃精确值,改用近似统计(如 HyperLogLog)或异步任务定时汇总
真正该警惕的不是怎么分,而是“要不要分”
很多项目在不到 500 万行、QPS
- 先确认瓶颈真在数据库:用
slow_query_log看是不是慢 SQL 没优化,而不是一上来就拆 - 垂直分表比水平分表安全得多:把
content、attachment拆出去,不影响 JOIN,还能提升缓存命中率 - 分表后最麻烦的永远不是写入,是数据订正、历史归档、跨库审计——这些事没人写进 demo 代码里,但线上天天发生
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











