用 Doctrine 迁移数据变慢
Migrating data with Doctrine becomes slow
我需要将数据从 db A 中的一个 table 导入到 db B(同一服务器)中的另一个 table,我选择了导入它的原则。
我使用的是 Symfony 命令,第一个循环一切都很好,只花了 0.04 秒,但随后开始变得越来越慢,几乎花了半个小时......
我正在考虑构建一个 shell 脚本来调用此 Symfony 命令并提供偏移量(我手动尝试过并保持相同的速度)。这是 docker 服务中的 运行,php 服务大约是 100% CPU,但是 mysql 服务是 10%
这是脚本的一部分:
class UserCommand extends Command
{
...
protected function execute(InputInterface $input, OutputInterface $output)
{
$container = $this->getApplication()->getKernel()->getContainer();
$this->doctrine = $container->get('doctrine');
$this->em = $this->doctrine->getManager();
$this->source = $this->doctrine->getConnection('source');
$limit = self::SQL_LIMIT;
$numRecords = 22690; // Hardcoded for debugging
$loops = intval($numRecords / $limit);
$numAccount = 0;
for ($i = 0; $i < $loops; $i++){
$offset = self::SQL_LIMIT * $i;
$users = $this->fetchSourceUsers($offset);
foreach ($users as $user) {
try{
$numAccount++;
$this->persistSourceUser($user);
if (0 === ($numAccount % self::FLUSH_FREQUENCY)) {
$this->flushEntities($output);
}
} catch(\Exception $e) {
//
}
}
}
$this->flushEntities($output);
}
private function fetchSourceUsers(int $offset = 0): array
{
$sql = <<<'SQL'
SELECT email, password, first_name
FROM source.users
ORDER by id ASC LIMIT ? OFFSET ?
SQL;
$stmt = $this->source->prepare($sql);
$stmt->bindValue(1, self::SQL_LIMIT, ParameterType::INTEGER);
$stmt->bindValue(2, $offset, ParameterType::INTEGER);
$stmt->execute();
$users = $stmt->fetchAll();
return $users;
}
}
如果 flush
花费的时间每隔 flush
就变长那么你忘记了 clear
实体管理器(对于批处理作业应该在 flush
之后发生).原因是您在实体管理器中不断积累实体,并且在每次提交期间,Doctrine 都会检查每个实体是否有更改(我假设您使用的是默认更改跟踪)。
I need to import data from one table in db A to another table in db B (same server) and I've chosen doctrine to import it.
除非你有一些与添加用户相关的复杂逻辑(即应用程序事件,在应用程序的另一端发生的事情,基本上需要一些其他的 PHP 代码来执行)那么你的选择很糟糕- Doctrine 不是为批处理而设计的(尽管如果你真的知道你在做什么,它也可以做得很好)。对于 "simple" 迁移,最好的选择是使用 DBAL。
我需要将数据从 db A 中的一个 table 导入到 db B(同一服务器)中的另一个 table,我选择了导入它的原则。
我使用的是 Symfony 命令,第一个循环一切都很好,只花了 0.04 秒,但随后开始变得越来越慢,几乎花了半个小时......
我正在考虑构建一个 shell 脚本来调用此 Symfony 命令并提供偏移量(我手动尝试过并保持相同的速度)。这是 docker 服务中的 运行,php 服务大约是 100% CPU,但是 mysql 服务是 10%
这是脚本的一部分:
class UserCommand extends Command
{
...
protected function execute(InputInterface $input, OutputInterface $output)
{
$container = $this->getApplication()->getKernel()->getContainer();
$this->doctrine = $container->get('doctrine');
$this->em = $this->doctrine->getManager();
$this->source = $this->doctrine->getConnection('source');
$limit = self::SQL_LIMIT;
$numRecords = 22690; // Hardcoded for debugging
$loops = intval($numRecords / $limit);
$numAccount = 0;
for ($i = 0; $i < $loops; $i++){
$offset = self::SQL_LIMIT * $i;
$users = $this->fetchSourceUsers($offset);
foreach ($users as $user) {
try{
$numAccount++;
$this->persistSourceUser($user);
if (0 === ($numAccount % self::FLUSH_FREQUENCY)) {
$this->flushEntities($output);
}
} catch(\Exception $e) {
//
}
}
}
$this->flushEntities($output);
}
private function fetchSourceUsers(int $offset = 0): array
{
$sql = <<<'SQL'
SELECT email, password, first_name
FROM source.users
ORDER by id ASC LIMIT ? OFFSET ?
SQL;
$stmt = $this->source->prepare($sql);
$stmt->bindValue(1, self::SQL_LIMIT, ParameterType::INTEGER);
$stmt->bindValue(2, $offset, ParameterType::INTEGER);
$stmt->execute();
$users = $stmt->fetchAll();
return $users;
}
}
如果 flush
花费的时间每隔 flush
就变长那么你忘记了 clear
实体管理器(对于批处理作业应该在 flush
之后发生).原因是您在实体管理器中不断积累实体,并且在每次提交期间,Doctrine 都会检查每个实体是否有更改(我假设您使用的是默认更改跟踪)。
I need to import data from one table in db A to another table in db B (same server) and I've chosen doctrine to import it.
除非你有一些与添加用户相关的复杂逻辑(即应用程序事件,在应用程序的另一端发生的事情,基本上需要一些其他的 PHP 代码来执行)那么你的选择很糟糕- Doctrine 不是为批处理而设计的(尽管如果你真的知道你在做什么,它也可以做得很好)。对于 "simple" 迁移,最好的选择是使用 DBAL。