【发布时间】:2023-04-05 11:19:01
【问题描述】:
我编写了一个 Symfony 命令来从 API 导入一些数据。它可以工作,但问题是当我在我的数据库中插入一个大 JSON 时,我的 PHP 内存使用量会增加。每次导入活动后,我的 unitOfWork 都会增加“2”。
我已经取消了我所有使用过的对象,当你想做大批量时,我已经阅读了 Symfony2 的文档:http://www.doctrine-project.org/blog/doctrine2-batch-processing.html
但是当我使用$em->clear() 时,我的实体管理器给出了这个错误:
注意:未定义索引:000000007b56ea7100000000e366c259 在 path-to-application\vendor\doctrine\lib\Doctrine\ORM\UnitOfWork.php 行 2228 中
这是我的完整代码:
/**
* @see Command
*/
protected function configure() {
$this
->setName('ks:user:runkeepersync')
->setDescription('Synchroniser les activités d\'un utilisateur runkeeper')
->setDefinition(array(
new InputArgument('access_token', InputArgument::REQUIRED, 'Access token'),
))
}
/**
* @see Command
*/
protected function execute(InputInterface $input, OutputInterface $output) {
$accessToken = $input->getArgument('access_token');
$em = $this->getContainer()->get('doctrine')->getEntityManager();
$UserHasServices = $em->getRepository('KsUserBundle:UserHasServices')->findOneByToken($accessToken);
if (!is_object($UserHasServices) ) {
echo "Impossible de trouver l'utilisateur qui possède le jeton ".$accessToken."";
}
$user = $UserHasServices->getUser();
$service = $UserHasServices->getService();
echo "avant de requérir l'api : ".memory_get_usage()."\n";
try {
$rkApi = $this->getContainer()->get('ks_user.runkeeper');
$rkApi->setAccessToken($accessToken);
$activities = $rkApi->getFitnessActivities(0,25);
$nbParPages = 25;
$nomberActivitites = $activities->size;
$aActivities = $activities->items;
$nbPages = floor ($nomberActivitites/$nbParPages);
$aEndurance = array("Running", "Cycling", "Mountain Biking", "Walking", "Hiking", "Downhill Skiing", "Cross-Country Skiing", "Snowboarding", "Skating","Wheelchair", "Rowing", "Elliptical", "Other");
$aEnduranceUnderWater = array("Swimming");
$enduranceOnEarthType = $em->getRepository('KsActivityBundle:SportType')->findOneByLabel("endurance");
if (!is_object($enduranceOnEarthType) ) {
echo "Impossible de trouver le type de sport d'endurance";
}
$enduranceUnderWaterType = $em->getRepository('KsActivityBundle:SportType')->findOneByLabel("endurance_under_water");
if (!is_object($enduranceUnderWaterType) ) {
echo "Impossible de trouver le type de sport d'endurance sous l'eau ";
}
echo "Après avoir récupéré 25 activités : ".memory_get_usage()."\n";
$a = 0;
for($i=0;$i<=$nbPages;$i++){
if($i!=0){
$activities = $rkApi->getFitnessActivities($i,25);
$aActivities = $activities->items;
}
foreach ($aActivities as $activity) {
$a = $a+1;
$codeSport = $this->formatNameSport($activity->type);
$sport = $em->getRepository('KsActivityBundle:Sport')->findOneByCodeSport($codeSport);
if (!is_object($sport) ) {
$sport = new \Ks\ActivityBundle\Entity\Sport();
$sport->setLabel($codeSport);
$sport->setCodeSport($codeSport);
$sport->setSportType($enduranceOnEarthType);
$em->persist($sport);
$em->flush();
}
$activityDetail = json_decode($rkApi->requestJSONHealthGraph($activity->uri));
if(in_array($activity->type, $aEndurance)){
$urlActivitieDetail = $activityDetail->activity;
$ActivitySessionEnduranceOnEarth = new \Ks\ActivityBundle\Entity\ActivitySessionEnduranceOnEarth($user);
isset($activity->total_distance)? $ActivitySessionEnduranceOnEarth->setDistance($activity->total_distance) : "";
isset($activity->duration)? $ActivitySessionEnduranceOnEarth->setDuration($this->secondesToTimeDuration($activity->duration)) : "";
isset($activity->start_time)? $ActivitySessionEnduranceOnEarth->setIssuedAt(new \DateTime($activity->start_time)) : "";
$ActivitySessionEnduranceOnEarth->setModifiedAt(new \DateTime('Now'));
$ActivitySessionEnduranceOnEarth->setSport($sport);
isset($activityDetail->total_calories)? $ActivitySessionEnduranceOnEarth->setCalories($activityDetail->total_calories) : "";
isset($activityDetail->climb)? $ActivitySessionEnduranceOnEarth->setElevationGain($activityDetail->climb) : "";
$maxElevation = 0;
$minElevation = 10000;
if(isset($activityDetail->path)){
foreach($activityDetail->path as $gpsPoint){
if($gpsPoint->altitude > $maxElevation){
$maxElevation = $gpsPoint->altitude;
}
if($gpsPoint->altitude < $minElevation){
$minElevation = $gpsPoint->altitude;
}
}
$ActivitySessionEnduranceOnEarth->setElevationMin($minElevation);
$ActivitySessionEnduranceOnEarth->setElevationMax($maxElevation);
}
$em->persist($ActivitySessionEnduranceOnEarth);
$em->flush();
//Pour chaque activité on a un identifiant relatif au service qu'on synchronise
$ActivityComeFromService = new \Ks\ActivityBundle\Entity\ActivityComeFromService();
$ActivityComeFromService->setActivity($ActivitySessionEnduranceOnEarth);
$ActivityComeFromService->setService($service);
$ActivityComeFromService->setIdWebsiteActivityService($activity->uri);
$ActivityComeFromService->setSourceDetailsActivity($rkApi->requestJSONHealthGraph($activity->uri));
$ActivityComeFromService->setTypeSource("JSON");
$em->persist($ActivityComeFromService);
$em->flush();
echo "Import de l'activite num ".$a." type :".$activity->type." effectue avec success \n";
unset($ActivitySessionEnduranceOnEarth);
unset($ActivityComeFromService);
echo "UnitOFWOrk -> ".$em->getUnitOfWork()->size()."\n";
}
if(in_array($activity->type, $aEnduranceUnderWater)){
$ActivitySessionEnduranceUnderWater = new \Ks\ActivityBundle\Entity\ActivitySessionEnduranceUnderWater($user);
isset($activity->total_distance)? $ActivitySessionEnduranceUnderWater->setDistance($activity->total_distance) : "";
isset($activity->duration)? $ActivitySessionEnduranceUnderWater->setDuration($this->secondesToTimeDuration($activity->duration)) : "";
isset($activity->start_time) && !empty($activity->start_time)? $ActivitySessionEnduranceUnderWater->setIssuedAt(new \DateTime($activity->start_time)) : "";
$ActivitySessionEnduranceUnderWater->setModifiedAt(new \DateTime('Now'));
$ActivitySessionEnduranceUnderWater->setSport($sport);
isset($activityDetail->total_calories)? $ActivitySessionEnduranceUnderWater->setCalories($activityDetail->total_calories) : "";
isset($activityDetail->notes)? $ActivitySessionEnduranceUnderWater->setDescription($activityDetail->notes) : "";
$em->persist($ActivitySessionEnduranceUnderWater);
$em->flush();
$ActivityComeFromService = new \Ks\ActivityBundle\Entity\ActivityComeFromService();
$ActivityComeFromService->setActivity($ActivitySessionEnduranceUnderWater);
$ActivityComeFromService->setService($service);
$ActivityComeFromService->setIdWebsiteActivityService($activity->uri);
$ActivityComeFromService->setSourceDetailsActivity($rkApi->requestJSONHealthGraph($activity->uri));
$ActivityComeFromService->setTypeSource("JSON");
$em->persist($ActivityComeFromService);
$em->flush();
echo "Import de l'activité num ".$a." type :".$activity->type." effectué avec succès\n";
unset($ActivitySessionEnduranceUnderWater);
unset($ActivityComeFromService);
}
echo "Après chaque activité : ".memory_get_usage()."\n";
unset($sport);
unset($activityDetail);
$em->clear();
}
}
} catch (\Exception $e) {
throw $e;
}
}
谢谢,@AdrienBrault。我用--env=prod --no-debug测试过,确实是消耗内存少了,但是内存还是增加了。我怎样才能真正清除实体管理器?并稳定记忆?
【问题讨论】:
-
减少命令内存使用的第一步是在 prod 环境中运行它们并禁用调试:
php app/console command --env=prod --no-debug -
谢谢,@AdrienBrault 我已经用 --env=prod --no-debug 进行了测试,确实它消耗的内存更少,但内存仍然增加......我该怎么做实体经理真的清楚吗?并稳定记忆?
-
@Hosh,为什么不在你不喜欢的答案下添加评论,解释为什么它不是一个好的解决方案?他们可能愿意提供进一步的帮助或建议。
-
我的错,请允许我解释一下。我对当前答案的问题是它不是解决方法,而是一种解决方法。我认为不需要重置管理器。显然有一个不同的问题正在破坏 Doctrine。虽然重置它(如果它完全有效,我还没有尝试过)作为一种解决方法很好,但这不是修复。无论如何,在生产中这样做听起来是一个非常糟糕的主意。希望这能让事情更清楚!
-
尝试从您的代码中删除
unset的使用。
标签: php symfony unit-of-work undefined-index memory-limit