Recursive MySQL işlev çağrısı çok fazla bellek yiyor ve ölür

7 Cevap php

Ben bir noktaya kadar çalışır ... aşağıdaki özyinelemeli işlevi var. Sorguları yaklaşık 100'ü Sonra bir kez komut daha fazla bellek için sorar, ve ben daha fazla bellek eklediğinizde, komut genellikle sadece (benim tarayıcı üzerinde beyaz bir ekran ile sonuna kadar) ölür.

public function returnPArray($parent=0,$depth=0,$orderBy = 'showOrder ASC'){



    $query = mysql_query("SELECT *, UNIX_TIMESTAMP(lastDate) AS whenTime 
        FROM these_pages 
        WHERE parent = '".$parent."' AND deleted = 'N' ORDER BY ".$orderBy."");

    $rows = mysql_num_rows($query);

    while($row = mysql_fetch_assoc($query)){

        // This uses my class and places the content in an array.
        MyClass::$_navArray[] = array(

            'id' => $row['id'], 
            'parent' => $row['parent']

        );

        MyClass::returnPArray($row['id'],($depth+1));   

    }
    $i++;

}

Herkes bana kaynak yoğun bu sorgu daha az olmasına yardımcı olabilir? Ya da bir şekilde ... çağrılar arasında belleği boşaltmak için bir yol bulmak.

7 Cevap

Beyaz ekran çünkü bir yığın taşması muhtemeldir. Eğer parent_id kendi kimliği var olan bir satır var mı? Sürünen gelen hata bu tür önlemek için nerede fıkrası AND id != '".(int)$parent."' eklemeyi deneyin ..

** EDIT: döngüsel başvurular için hesap için, gibi bir şey için atama değiştirerek deneyin:

while($row = mysql_fetch_assoc($query)){
    if (isset(MyClass::$_navArray[$row['id']])) continue;

    MyClass::$_navArray[$row['id']] = array(
        'id' => $row['id'], 
        'parent' => $row['parent']
    );
    MyClass::returnPArray($row['id'],($depth+1));
}

(Ben satır sayısı 0 olup olmadığını yönteminden dönmek gerek yok sanırım) bir noktada özyinelemeye durmamalı? Kodundan ben returnPArray için sonsuz özyinelemeli aramaları görmek yayınlanmıştır.

Sana şunu sorayım ... Sadece sayfaların bir ağaç dışında inşa etmeye çalışıyoruz? Eğer öyleyse, bir nihai ebeveyn çağırabilir hiyerarşisi boyunca bazı nokta var? Ben hemen ebeveyne ek olarak nihai ana id saklamak, db buklesini saklarken sen db karşı herhangi bir özyineleme ve yineleme gerekmez gibi çok daha hızlı geri almak için yapar bulduk.

Bu denormalizasyon biraz, ama sadece küçük bir parçasıdır, ve özyineleme veya db vs yineleme daha denorm daha iyidir.

İhtiyaçlarınızı daha karmaşık iseniz, ihtiyacınız daha ağacın daha almak ve sadece ihtiyaç düğümler / satır almak için yineleme yapmak için app kod kullanmak daha iyi olabilir. Çoğu uygulama kod yineleme / özyinelemenin herhangi DB üstündür.

Büyük olasılıkla aktif sorgu sonuç kümeleri üzerinde aşırı yükleme ediyoruz. Dediğiniz gibi, derin özyineleme içine yaklaşık 100 yineleme alıyorsanız, eğer, o 100 sorgular / resultsets açık var demektir. Her sorgu yalnızca bir satır döndürür bile ikinci çağrıyı (ki yanlış dönecekti) almak kadar, tüm resultset açık tutulur. Bunu İkinci çağrıyı yapmak için geri herhangi bir seviyeye asla, böylece sadece yeni sorgular kapalı ateş ve yeni sonuç kümelerini açma tutmak.

Eğer ağaç seviye başına gereken tek bir sonuç ile, basit bir kırıntı izi için gidiyoruz eğer, o zaman sonuç kümesi üzerinde yineleme için bir süre () döngü yapmıyor öneririm. Sonra yinelemeli çağrıyı yapmak SONRA, mysql_free_result() ile resultset kapatın, her bir seviye için kayıt getir.

Aksi takdirde, bir genişlik-ilk sorgu yöntemine geçmeyi deneyin, ve yine, her bir ağaç düzeyde oluşturduktan sonra resulset özgür.

Neden bir özyinelemeli işlev kullanıyorsunuz? Ben koduna baktığınızda sadece tüm kayıtların çocuk ve ebeveyn kimliği hem de içerecek bir tablo oluştururken, sanki görünüyor. Bu bir sonucu olarak istediğiniz buysa bile özyinelemeye gerekmez. Basit bir seçme, parent_id filtreleme (ama muhtemelen üzerinde sipariş) yapacak ve sadece bir kez üzerinde yineleme değil.

Aşağıdaki muhtemelen mevcut özyinelemeli işlev olarak aynı sonuçları dönecektir:

public function returnPArray($orderBy = 'showOrder ASC'){
    $query = mysql_query("SELECT *, UNIX_TIMESTAMP(lastDate) AS whenTime 
        FROM these_pages 
        WHERE deleted = 'N' ORDER BY parent ASC,".$orderBy."");

    $rows = mysql_num_rows($query);

    while($row = mysql_fetch_assoc($query)){

        // This uses my class and places the content in an array.
        MyClass::$_navArray[] = array(

            'id' => $row['id'], 
            'parent' => $row['parent']

        );
    }
}

Ben bir sorgudaki tüm satırları almak önermek ve saf PHP kullanarak ağaç yapısını kurmak istiyorum:

$nodeList = array();
$tree     = array();

$query = mysql_query("SELECT *, UNIX_TIMESTAMP(lastDate) AS whenTime 
    FROM these_pages WHERE deleted = 'N' ORDER BY ".$orderBy."");
while($row = mysql_fetch_assoc($query)){
    $nodeList[$row['id']] = array_merge($row, array('children' => array()));
}
mysql_free_result($query);

foreach ($nodeList as $nodeId => &$node) {
    if (!$node['parent_id'] || !array_key_exists($node['parent_id'], $nodeList)) {
        $tree[] = &$node;
    } else {
        $nodeList[$node['parent_id']]['children'][] = &$node;
    }
}
unset($node);
unset($nodeList);

Gerektiği gibi ayarlayın.

Bir kaç sorun vardır.

  1. Zaten hafıza sorunu fark ettim. Sen ini_set kullanarak sınırsız bellek ayarlayabilirsiniz ('memory_limit', -1).
  2. Komut dosyası maksimum yürütme süresini aşıyor ve display_errors kapalı veya error_reporting E_NONE ayarlanır ya çünkü beyaz bir ekran almak nedenidir. Sen set_time_limit (0) kullanarak sınırsız yürütme zamanı ayarlayabilirsiniz.
  3. Hatta "sınırsız" bellek ve "sınırsız" bir süre ile, yine tabii ki sizin sunucu ve kendi kıymetli zaman sınırları ile kısıtlı. Seçtiğiniz algoritma ve veri modeli iyi ölçek olmayacak ve bu bir üretim web sitesi içindir eğer, o zaman zaten zaman ve bellek bütçe şişmiş.

# 3 çözümü daha etkin bir algoritma destekleyen iyi bir veri modelini kullanmaktır.

Sizin fonksiyonu kötü adlandırılır, ama "belirli bir sayfanın tüm anne babaların bir dizi dönmek" anlamına gelir tahmin ediyorum.

Eğer yapmak istediğiniz ne varsa, o zaman daha verimli sorgulama için bir strateji olarak Modified Pre-order Tree Traversal check out. Bu davranış, zaten böyle kullanımı özellikle kolay Doktrini ORM gibi bazı çerçeveler içine inşa edilmiştir.