Symfony2 中 $em->clear() 时出现未定义的索引错误

我写了一个Symfony命令来从API导入一些数据。它的工作原理是,当我在数据库中插入一个大的JSON时,我的PHP内存使用量会增加。我的单位工作增加“2”到每次活动导入后。

我已经取消了所有使用的对象,并且当您想进行大量批处理时,我已经阅读了Symfony2的文档:http://www.doctrine-project.org/blog/doctrine2-batch-processing.html

但是当我使用我的实体管理器时,会给出这个错误:$em->clear()

注意:未定义索引:000000007b56ea7100000000e366c259 在应用程序路径\供应商\原则\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


答案 1

Symfony在开发环境中记录所有SQL查询,因此首先您需要禁用它

// disable logger
$em->getConnection()->getConfiguration()->setSQLLogger(null);

您可以在实体上使用事件侦听器,这也可能增加内存使用率。您可以像这样禁用它们

// remove all listeners
foreach ($em->getEventManager()->getListeners() as $event => $listeners) {
    foreach ($listeners as $listener) {
        $em->getEventManager()->removeEventListener($event, $listener);
    }
}

从代码中删除,不需要它们,因为您清除了循环的每一步实体管理器。unset

// save and clear
$em->flush();
$em->getUnitOfWork()->clear();

请记住,该原则可以优化您的查询,如果您将查询分组为一个,则可以提高性能。因此,最佳做法是对数据的某些部分执行一次。例如:flushflush

// collect 100 entities and then save them
if (($i % 100) == 0) {
    $em->flush();
    $em->getUnitOfWork()->clear();
}

答案 2

尝试使用以下命令重置实体管理器:

$this->getContainer()->get('doctrine')->resetEntityManager();

然后:

$em = $this->getContainer()->get('doctrine')->getEntityManager();

推荐