【问题标题】:Undefined index error upon $em->clear() in Symfony2Symfony2 中 $em->clear() 的未定义索引错误
【发布时间】: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


【解决方案1】:

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

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

然后:

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

【讨论】:

    【解决方案2】:

    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();
    

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

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

    【讨论】:

    • 这不完全是解决方案。但是您的解决方案使我找到了解决方案。事实证明,我正在覆盖的事件侦听器之一(通过为其重载类参数)正在做一些导致这种情况的事情。删除了该定义(并且仅手动删除了该事件侦听器)解决了这个问题。我会给你赏金。
    • 你是如何摆脱“未定义索引”的?你能为你谈到的扩展解决方案编写代码吗?
    • 也使用 getUntiOfWork 而不是普通的 clear 调用我得到未定义的索引 - 有人想出一个真正的解决方案吗?
    猜你喜欢
    • 1970-01-01
    • 2012-09-08
    • 2014-02-01
    • 2011-12-12
    • 1970-01-01
    • 1970-01-01
    • 2014-01-17
    • 2011-02-21
    • 2016-08-17
    相关资源
    最近更新 更多