Webman分库分表必须解决三个硬伤:date('Ym')被Worker缓存导致跨月查不到新表、PDO跨库复用引发“MySQL server has gone away”、chunk()默认走静态表名致全表扫描;须用业务时间戳动态生成表名、每库独立禁持久化PDO实例、显式指定分表执行查询。

Webman 做分库分表,不是配好连接、改个表名就跑得动——真要扛住日均千万级写入或亿级跳转,必须直面三个硬伤:date('Ym')被 Worker 缓存、PDO 实例跨库复用、chunk()查错表。绕开它们,才谈得上“实战”。
按月分表时 date('Ym') 为什么总查不到当月数据?
现象:系统跨月第一天,新日志死活写不进 log_202606,还一直往 log_202605 写。
原因:Webman Worker 常驻,date('Ym') 在进程启动时就被 PHP 编译器缓存了,后续所有请求都返回同一个值。
- 必须用业务时间戳动态计算,比如
$table = 'log_' . date('Ym', $log['create_time']),绝不能用time()或静态属性缓存 - ThinkORM 模型里别写
protected $table = 'log_' . date('Ym')——这行代码在类加载时就执行,锁死整个月份 - 建表逻辑要放构造函数里,每次实例化都检查:
if (!Db::schema()->hasTable($this->table)) { Db::update("CREATE TABLE {$this->table} LIKE log_base"); }
分库场景下 PDO 连接为什么总报 MySQL server has gone away?
现象:凌晨三点突然大量报错,但数据库本身没宕,连接数也没满。
Webman 2.2.0版本强化了 TCP/UDP 服务支持,优化路由组管理,并增强异步任务处理能力。结合协程与连接池技术,Webman 能轻松应对高并发场景,适用于网站、接口服务、即时通讯、物联网及游戏开发,兼具高性能、灵活扩展与稳定可靠,是多场景 PHP 服务开发的理想选择。
原因:Worker 长期持有已失效的持久化连接,或多个分库共用一个 PDO 实例导致事务串库。
- 每个分库必须初始化独立 PDO 实例,命名明确,例如:
Container::set('order_db_0', new PDO($dsn0, $user, $pass, [PDO::ATTR_PERSISTENT => false])) - 绝对禁用
PDO::ATTR_PERSISTENT => true——Webman 自带连接池已处理复用,再开持久连接会快速耗尽 MySQL 连接数 - 别在
onWorkerStart里把 PDO 赋给static $pdo——多进程不共享,PHP 8.5.5 还可能因序列化失败直接 crash
chunk() 和 cursor() 查询为何越分表越慢?
现象:查某个月的日志,响应从 50ms 涨到 2s,EXPLAIN 显示全表扫描所有分表。
原因:ThinkORM 的 chunk() 默认走模型静态 $table,根本不知道你要查的是 log_202606 还是 log_202605。
- 必须显式指定目标表:
Db::table($table_name)->where(...)->chunk(500, function ($items) { ... }) - 导出历史数据优先用
cursor()+yield,避免chunk()内部累积临时数组吃光内存 - 禁止对分表做
UNION ALL扫全库——跳转短链这类低延迟场景,必须靠短码前缀哈希(如hash('ab') % 10)一次路由到唯一库+表
真正麻烦的从来不是怎么写分表逻辑,而是怎么让每个请求都干净地结束:表名不残留、连接不泄漏、上下文不污染。Worker 常驻的代价,得用每次请求都重算、每个库都隔离、每条查询都显式指定来付。

















