webman分库分表需确保路由可控、连接不泄漏、生命周期不污染:按月分表须实时计算表名(如date('ym', $createtime)),pdo连接须分库隔离且禁用持久化,chunk/cursor查询须显式指定目标表名,建表需校验权限与引擎。

Webman 做分库分表不是加个中间件就完事,关键在路由可控、连接不泄漏、生命周期不污染。真要扛住日均千万级日志或百万级订单,得从表名生成、PDO 实例隔离、请求上下文清理三处下手。
按月分表时,date('Ym') 不能直接硬编码进 SQL
很多 Webman 项目一上来就写 $table = 'log_' . date('Ym') 拼表名,结果发现跨月第一天查不到数据——因为 PHP 进程常驻,date() 在 Worker 启动时就被缓存了(尤其用 date_default_timezone_set() 后更隐蔽)。必须每次请求都重新计算:
- 在控制器或服务层调用前实时生成:
$table = 'log_' . date('Ym', $createTime),其中$createTime来自业务数据本身(如日志时间戳),而非time() - 禁止在类属性或静态变量里缓存表名,比如
private static $table = 'log_' . date('Ym')—— 这会导致整个 Worker 生命周期锁死在一个月份 - 如果用 ThinkORM,需重写
getTable()方法,传入当前记录的create_time字段值动态返回表名,而不是依赖模型静态配置
分库场景下,PDO 连接不能复用同一个实例
Webman 的常驻特性让连接池成了双刃剑:没管好就会连接堆积、事务串库、甚至 MySQL server has gone away。常见错误是把所有分库连接塞进一个全局数组然后随机取:
- 每个分库必须初始化独立的
PDO对象,且配置PDO::ATTR_PERSISTENT => false—— Webman 自带连接池已处理复用,再开持久连接反而触发 MySQL 连接数耗尽 - 避免在
onRequest中 new PDO,应提前在start.php或服务提供者中初始化并绑定到容器,例如:Container::set('order_db_0', new PDO($dsn0, $user, $pass)) - 跨库事务(如用户库 + 订单库)无法保证原子性,Webman 里别硬上
beginTransaction(),改用最终一致性:先写本地库,再发消息队列异步补全关联库
chunk() 和 cursor() 在分表查询中必须显式指定表名
ThinkORM 的 chunk() 默认走主模型表名,一旦做了水平分表,不干预就会全表扫描所有子表(比如 orders_202601, orders_202602…),性能断崖式下跌。
- 查某个月份数据时,必须先算出目标表名,再用
Db::table($table)->where(...)->chunk(500, function ($items) {...}) - 导出历史日志这类大结果集,优先用
cursor()+yield,避免chunk()内部的临时数组累积内存 —— Webman 中一个 Worker 内存涨到 512MB 以上基本就是chunk()没切表+没释放导致的 - 禁止对分表模型调用
with()关联查询,比如OrderModel::with('user')—— 分表后user在另一库,ORM 无法自动路由,会报Table not found或静默漏数据
分表元数据变更容易被忽略:新表创建与权限同步
Webman 不像 Laravel 有 php artisan migrate 自动推表,上线新分表(如 log_202606)靠代码自动建表时,最容易卡在两个地方:
- MySQL 用户对新表名无
CREATE权限,报错Access denied for user ... to database,但日志里只显示 PDOException 而不提具体缺失权限 - 建表 SQL 里漏掉
ENGINE=InnoDB DEFAULT CHARSET=utf8mb4,导致新表用 MyISAM 引擎,后续INSERT ... ON DUPLICATE KEY UPDATE失效 - Webman 的定时器(如每月 1 号凌晨建新表)若放在
onWorkerStart,多个 Worker 会并发执行建表语句,需加 Redis 分布式锁或检查表是否存在再建
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











