框架:thinkphp
场景:在脚本中循环查询数据并处理,查询数据使用的model中含有分表逻辑,若分表不存在则自动创建分表。
model代码:
class TestTableModel {
public function getTable($date) {
$r = Db::query('show tables like "test_table_{$date}"');
if (empty($r)) { // 若表不存在,则创建
Db::execute('创建 test_table_{$date} 表');
}
// 使用Db对象
return Db::table('test_table_{$date}');
}
}
脚本代码:
$date = '20230506';
$page = 1; // 分页页码
while (1) {
$db = (new TestTableModel)->getTable($date)->page($page)->select();
++$page;
// 进行一些处理
(......省略)
}
这个循环只要一执行就瞬间报出 2006, MySQL server has gone away 的错误。最一开始我以为是和mysql的连接超时导致的,但后来将model中的 show tables like 这句去掉之后,代码就能正常运行了,而且运行很长很长时间也不会报错。
调整后能正确运行的model:
class TestTableModel {
public function getTable($date) {
// 使用Db对象
return Db::table('test_table_{$date}');
}
}
所以现在的问题就是,为什么加上 show tables like 这句查询就会导致 2006, MySQL server has gone away ? 然而如果这个model不在循环中使用也不会报错,而且在mysql中执行show tables like速度也很快,有点搞不懂。当前查询中的分表是存在的,而且数据库的数据表总量也就近百个。原因在哪里呢?
可能的原因会很多,可以参考 MySQL 手册 中的说明去解决。
如果你只想简单解决问题,那有更简单的方法,就是自动重连,参考 ThinkPHP 手册中关于 断线重连 的配置介绍。
另外,你这种方式会存在一些意外风险,在 MySQL 中,DDL 语句是会自动提交事务的,如果你的代码在事务中运行,且这个表不存在的时候,走到了创建表的语句,就有可能导致事务被意外的提交了,基于这个原因,你得建表操作最好在外部完成。
除此之外,在一些情况下,你还可以有其他的优化方案来查询你这个 show tables,你可以在循环外面使用:
show tables like 'test_table_%'
,将指定前缀的表取出来,存为变量,然后用 php 去判断就好了,而不是现在这样每次都查询。如果循环比较多,可能在这个期间创建了表,那你还可以考虑每 N 个或者每 N 秒后,执行一下上面的语句,重新覆盖一下。