2012-07-09 27 views
19

Tôi đã viết lệnh Symfony để nhập một số dữ liệu từ API. Nó hoạt động nhưng vấn đề là việc sử dụng bộ nhớ PHP của tôi tăng lên khi tôi chèn một JSON lớn vào cơ sở dữ liệu của mình. Và unitOfWork của tôi tăng thêm '2' sau mỗi lần nhập.

tôi có đã unset tất cả các đối tượng của tôi được sử dụng, và tôi đã đọc tài liệu của Symfony2 khi bạn muốn làm hàng loạt đồ sộ: http://www.doctrine-project.org/blog/doctrine2-batch-processing.html

Nhưng khi tôi sử dụng $em->clear() quản lý thực thể của tôi cho lỗi này:

Notice: Undefined index: 000000007b56ea7100000000e366c259 in path-to-application\vendor\doctrine\lib\Doctrine\ORM\UnitOfWork.php line 2228

đây là mã hoàn chỉnh của tôi:

/** 
* @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; 
    } 
} 

Cảm ơn, @AdrienBrault. Tôi đã thử nghiệm với --env=prod --no-debug, và đúng là nó tiêu thụ ít bộ nhớ hơn, nhưng bộ nhớ vẫn tăng. Làm thế nào tôi có thể thực sự rõ ràng người quản lý thực thể? và ổn định trí nhớ?

+0

Bước đầu tiên để giảm sử dụng bộ nhớ trên lệnh là để chạy chúng trên môi trường prod và với debug bị vô hiệu hóa: 'php ứng dụng/console lệnh --env = prod --no-debug' – AdrienBrault

+1

Cảm ơn , @AdrienBrault Tôi đã thử nghiệm với --env = prod --no-debug, và nó là sự thật rằng nó tiêu thụ ít bộ nhớ hơn, nhưng bộ nhớ vẫn tăng ... Làm thế nào tôi có thể làm để thực sự rõ ràng quản lý thực thể? và ổn định trí nhớ? – psylo66

+0

@Hosh, tại sao không thêm bình luận dưới câu trả lời bạn không thích, giải thích tại sao nó không phải là giải pháp tốt? Họ có thể sẵn lòng trợ giúp hoặc tư vấn thêm. – halfer

Trả lời

1

Cố gắng thiết lập lại quản lý thực thể có:

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

và sau đó:

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

Symfony ghi lại tất cả các truy vấn SQL trong môi trường dev, vì vậy trước hết bạn cần phải vô hiệu hóa nó

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

Bạn có thể sử dụng trình xử lý sự kiện trên các thực thể, nó cũng có thể làm tăng mức sử dụng bộ nhớ. Bạn có thể vô hiệu hóa chúng như vậy

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

Di unset từ mã của bạn, không có nhu cầu đối với họ, như bạn rõ ràng quản lý thực thể từng bước của vòng lặp của bạn.

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

Ghi học thuyết có thể tối ưu hóa truy vấn của bạn, và cải thiện hiệu suất hoạt nếu bạn nhóm truy vấn vào một flush. Vì vậy, cách thực hành tốt nhất là thực hiện flush một lần trên một số phần dữ liệu của bạn. Ví dụ:

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

Đây không phải là giải pháp chính xác. Nhưng giải pháp của bạn đã dẫn tôi đến giải pháp. Nó chỉ ra một trong những người nghe sự kiện tôi đã ghi đè (bằng cách quá tải tham số lớp cho nó) đã làm một cái gì đó gây ra điều này. Sau khi xóa định nghĩa đó (và chỉ xóa thủ công trình xử lý sự kiện đó) đã giải quyết được vấn đề này.Tôi sẽ cung cấp cho bạn tiền thưởng. –

+0

Bạn đã loại bỏ "chỉ mục không xác định" như thế nào? Và bạn có thể viết mã cho giải pháp mở rộng mà bạn đã nói đến không? –

Các vấn đề liên quan