Symfony 3 批量插入数据速度过慢的优化方案求助
Symfony Doctrine批量插入性能优化方案
核心性能问题定位
你提供的代码存在3个严重影响执行效率的问题:
- 循环内每次执行
flush():每次flush()都会触发一次数据库事务提交,单次IO开销被放大N倍(N为数据条数),是性能低下的核心原因 - 循环内单次执行查询:每次
findOneBy都会发起单次数据库查询,产生严重的N+1查询问题 - 未限制实体管理器内存占用:Doctrine会保留所有已持久化的实体在内存中,数据量大会导致内存溢出同时拖慢执行速度
- 额外注意:原代码存在变量拼写错误(
$entitty和$entity不一致),需先修正避免业务逻辑异常
可落地优化方案
批量提交事务
累计插入200~500条(可根据单条数据大小调整)后再执行一次flush(),大幅降低事务提交的IO开销前置批量查询去重
提前把所有需要校验的phone批量查询出来存入数组,用内存校验替代数据库查询,消除N+1问题分批清理实体管理器
每批flush之后执行clear()清空EntityManager的实体缓存,降低内存占用关闭SQL日志
批量任务执行前关闭Doctrine SQL日志,减少额外性能开销:$manager->getConnection()->getConfiguration()->setSQLLogger(null);
优化后代码示例
$manager = $this->getContainer()->get('doctrine')->getEntityManager(); // 关闭SQL日志减少额外开销 $manager->getConnection()->getConfiguration()->setSQLLogger(null); $batchSize = 300; // 每300条提交一次,可根据实际场景调整 $count = 0; // 前置批量查询所有已存在的phone,存入哈希表做内存校验 $allPhones = array_map(fn($value) => $value->getPhone(), $data); $existPhones = $stkModel->getRepository()->createQueryBuilder('s') ->select('s.phone') ->where('s.phone IN (:phones)') ->setParameter('phones', $allPhones) ->getQuery() ->getSingleColumnResult(); $existPhoneMap = array_flip($existPhones); foreach ($data as $value) { // 内存校验替代循环查询数据库 if (isset($existPhoneMap[$value->getPhone()])) { $stk = $stkModel->getEntity(); $stk->setName('mina'); $stk->setStatus(1); $manager->persist($stk); // 无需flush即可拿到stk自增ID(仅适用于MySQL自增主键场景) $manager->getUnitOfWork()->computeChangeSets(); $title = $titleModel->getEntity(); $title->setName('dev'); $title->setOrder(2); $title->setStk($stk); // 直接关联实体,无需额外查询stkId $manager->persist($title); // 可直接获取title ID做后续业务处理 // $titleId = $title->getId(); $count++; // 达到批次阈值统一提交 if (($count % $batchSize) === 0) { $manager->flush(); $manager->clear(); // 清空实体缓存释放内存 } } } // 提交剩余不足一批的数据 $manager->flush(); $manager->clear();
额外优化建议
- 涉及的查询字段(phone、stkId)需确保在数据库中添加了索引,避免全表扫描拖慢查询速度
- 可根据业务对数据安全性的要求,适当调整数据库
innodb_flush_log_at_trx_commit参数进一步提升写入性能 - 若数据量超过10万条,建议使用多进程分块处理,进一步缩短执行时间
内容的提问来源于stack exchange,提问作者json
相关产品推荐
相关产品推荐

