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

发布于 2024-09-03 00:53:10 字数 792 浏览 3 评论 0原文

我有以下递归函数,它可以工作......直到某一点。然后,一旦查询超过 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++;

}

谁能帮助我减少这个查询的资源消耗?或者找到一种方法在调用之间释放内存......以某种方式。

I have the following recursive function which works... up until a point. Then the script asks for more memory once the queries exceed about 100, and when I add more memory, the script typically just dies (I end up with a white screen on my browser).

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

}

Can anyone help me make this query less resource intensive? Or find a way to free up memory between calls... somehow.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

葬心 2024-09-10 00:53:10

白屏可能是由于堆栈溢出造成的。你是否有一行的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));
}

The white screen is likely because of a stack overflow. Do you have a row where the parent_id is it's own id? Try adding AND id != '".(int)$parent."' to the where clause to prevent that kind of bug from creeping in...

**EDIT: To account for circular references, try modifying the assignment to something like:

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));
}
熊抱啵儿 2024-09-10 00:53:10

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

Shouldn't you stop recursion at some point (I guess you do need to return from method if the number of rows is 0) ? From the code you posted I see an endless recursive calls to returnPArray.

糖粟与秋泊 2024-09-10 00:53:10

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

这是一种非规范化,但只是一小部分,并且与数据库相比,去规范化比递归或迭代更好。

如果您的需求更复杂,最好检索比您需要的更多的树,并使用应用程序代码进行迭代以仅获取您需要的节点/行。大多数应用程序代码在迭代/递归方面远远优于任何数据库。

Let me ask you this... are you just trying to build out a tree of pages? If so, is there some point along the hierarchy that you can call an ultimate parent? I've found that when storing tress in a db, storing the ultimate parent id in addition to the immediate parent makes it much faster to get back as you don't need any recursion or iteration against the db.

It is a bit of denormalization, but just a small bit, and it's better to denorm than to recurse or iterate vs the db.

If your needs are more complex, it may be better to retrieve more of the tree than you need and use app code to iterate through to get just the nodes/rows you need. Most application code is far superior to any DB at iteration/recursion.

暖伴 2024-09-10 00:53:10

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

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

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

Most likely you're overloading on active query result sets. If, as you say, you're getting about 100 iterations deep into the recursion, that means you've got 100 queries/resultsets open. Even if each query only returns one row, the whole resultset is kept open until the second fetch call (which would return false). You never get back to any particular level to do that second call, so you just keep firing off new queries and opening new result sets.

If you're going for a simple breadcrumb trail, with a single result needed per tree level, then I'd suggest not doing a while() loop to iterate over the result set. Fetch the record for each particular level, then close the resultset with mysql_free_result(), THEN do the recursive call.

Otherwise, try switching to a breadth-first query method, and again, free the resulset after building each tree level.

小草泠泠 2024-09-10 00:53:10

为什么要使用递归函数?当我查看代码时,看起来好像您只是创建一个表,其中包含所有记录的子 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']

        );
    }
}

Why are you using a recursive function? When I look at the code, it looks as though you're simply creating a table which will contain both the child and parent ID of all records. If that's what you want as a result then you don't even need recursion. A simple select, not filtering on parent_id (but probably ordering on it) will do, and you only iterate over it once.

The following will probably return the same results as your current recursive function :

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

        );
    }
}
夏见 2024-09-10 00:53:10

我建议在一个查询中获取所有行并使用纯 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);

根据需要进行调整。

I'd suggest getting all rows in one query and build up the tree-structure using pure 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);

Adjust as needed.

水晶透心 2024-09-10 00:53:10

有一些问题。

  1. 您已经注意到内存问题了。您可以使用 ini_set('memory_limit', -1) 设置无限内存。
  2. 出现白屏的原因是脚本超出了最大执行时间,并且您关闭了 display_errors 或将 error_reporting 设置为 E_NONE。您可以使用 set_time_limit(0) 设置无限的执行时间。
  3. 即使拥有“无限”内存和“无限”时间,您仍然明显受到服务器和您自己宝贵时间的限制。您选择的算法和数据模型无法很好地扩展,如果这是用于生产网站,那么您已经耗尽了时间和内存预算。

#3 的解决方案是使用更好的数据模型来支持更高效的算法。

您的函数命名得很糟糕,但我猜它的意思是“返回特定页面的所有父级的数组”。

如果这就是您想要做的,请查看修改后的前序树遍历< /a> 作为更有效查询的策略。这种行为已经内置到一些框架中,例如 Doctrine ORM,这使得它特别易于使用。

There are a few problems.

  1. You already noticed the memory problem. You can set unlimited memory by using ini_set('memory_limit', -1).
  2. The reason you get a white screen is because the script exceeds the max execution time and you either have display_errors turned off or error_reporting is set to E_NONE. You can set unlimited execution time by using set_time_limit(0).
  3. Even with "unlimited" memory and "unlimited" time, you are still obviously constrained by the limits of your server and your own precious time. The algorithm and data model that you have selected will not scale well, and if this is meant for a production website, then you have already blown your time and memory budget.

The solution to #3 is to use a better data model which supports a more efficient algorithm.

Your function is named poorly, but I'm guessing it means to "return an array of all parents of a particular page".

If that's what you want to do, then check out Modified Pre-order Tree Traversal as a strategy for more efficient querying. This behavior is already built into some frameworks, such as Doctrine ORM, which makes it particularly easy to use.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文