递归MySQL函数调用会占用太多内存并死掉

时间:2010-05-28 16:43:46

标签: php mysql recursion

我有以下递归函数,直到某一点为止。然后,一旦查询超过大约100,脚本会要求更多内存,当我添加更多内存时,脚本通常会死掉(我最终在浏览器上显示白屏)。

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++;

}

任何人都可以帮我减少资源密集吗?或者找到一种方法来释放呼叫之间的内存...不知何故。

7 个答案:

答案 0 :(得分:1)

白屏很可能是因为堆栈溢出。你有一行parent_id是它自己的id吗?尝试将AND id != '".(int)$parent."'添加到where子句中,以防止此类错误蔓延到...

**编辑:要考虑循环引用,请尝试将分配修改为:

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

答案 1 :(得分:0)

你不应该在某个时候停止递归(如果行数是0,我想你确实需要从方法返回)?从你发布的代码中我看到了对returnPArray的无休止的递归调用。

答案 2 :(得分:0)

让我问你这个......你只是想要建一个页面树吗?如果是这样,层次结构中是否有一点可以称为最终父级?我发现当在数据库中存储tress时,除了直接父节点之外还存储最终的父节点会使得返回更快,因为你不需要对数据库进行任何递归或迭代。

这是一个非规范化,但只是一点点,并且最好是声明而不是递归或迭代与数据库。

如果您的需求更复杂,最好是检索比您需要的树更多的树,并使用应用程序代码迭代以获得您需要的节点/行。在迭代/递归时,大多数应用程序代码都远远优于任何DB。

答案 3 :(得分:0)

您很可能在活动查询结果集上超载。如果,正如您所说,您在递归过程中获得了大约100次迭代,这意味着您已经打开了100个查询/结果集。即使每个查询只返回一行,整个结果集也会保持打开状态,直到第二次获取调用(返回false)。您永远不会回到任何特定级别来执行第二次调用,因此您只需继续触发新查询并打开新结果集。

如果你想要一个简单的痕迹路径,每个树级需要一个结果,那么我建议不要做一个while()循环迭代结果集。获取每个特定级别的记录,然后使用mysql_free_result()关闭结果集,然后执行递归调用。

否则,尝试切换到广度优先查询方法,并在构建每个树级别后再次释放结果集。

答案 4 :(得分:0)

为什么使用递归函数?当我查看代码时,看起来好像只是创建一个包含所有记录的子ID和父ID的表。如果这就是你想要的结果那么你甚至不需要递归。一个简单的选择,而不是对parent_id进行过滤(但可能是对它进行排序),你只需要迭代一次。

以下内容可能会返回与当前递归函数相同的结果:

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']

        );
    }
}

答案 5 :(得分:0)

我建议在一个查询中获取所有行,并使用纯PHP构建树结构:

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

根据需要进行调整。

答案 6 :(得分:0)

有一些问题。

  1. 您已经注意到了内存问题。您可以使用ini_set('memory_limit', - 1)来设置无限内存。
  2. 您获得白屏的原因是脚本超出了最大执行时间,并且您已关闭display_errors或将error_reporting设置为E_NONE。您可以使用set_time_limit(0)设置无限的执行时间。
  3. 即使拥有“无限”的记忆和“无限”时间,您仍然显然受到服务器限制和宝贵时间的限制。您选择的算法和数据模型不能很好地扩展,如果这是针对生产网站的,那么您已经浪费了时间和内存预算。
  4. #3的解决方案是使用更好的数据模型,该模型支持更高效的算法。

    你的功能命名很差,但我猜这意味着“返回一个特定页面的所有父母的数组”。

    如果这是您想要做的,那么请查看Modified Pre-order Tree Traversal作为更有效查询的策略。这种行为已经内置到一些框架中,例如Doctrine ORM,这使得它特别易于使用。