递归MySQL函数调用占用过多内存并死掉
我有以下递归函数,它可以工作......直到某一点。然后,一旦查询超过 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
白屏可能是由于堆栈溢出造成的。你是否有一行的parent_id是它自己的id?尝试将
AND id != '".(int)$parent."'
添加到 where 子句以防止此类错误蔓延...**编辑:为了考虑循环引用,尝试将分配修改为:
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:
难道你不应该在某个时刻停止递归吗(我想如果行数为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.
让我问你这个......你只是想构建一个页面树吗?如果是这样,那么层次结构中是否存在可以称为最终父级的点?我发现,当将 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.
很可能您正在超载活动查询结果集。如果,正如您所说,您在递归中进行了大约 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.
为什么要使用递归函数?当我查看代码时,看起来好像您只是创建一个表,其中包含所有记录的子 ID 和父 ID。如果这就是您想要的结果,那么您甚至不需要递归。一个简单的选择,不过滤parent_id(但可能对其进行排序)就可以了,并且您只需迭代一次。
以下可能会返回与当前递归函数相同的结果:
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 :
我建议在一个查询中获取所有行并使用纯 PHP 构建树结构:
根据需要进行调整。
I'd suggest getting all rows in one query and build up the tree-structure using pure PHP:
Adjust as needed.
有一些问题。
#3 的解决方案是使用更好的数据模型来支持更高效的算法。
您的函数命名得很糟糕,但我猜它的意思是“返回特定页面的所有父级的数组”。
如果这就是您想要做的,请查看修改后的前序树遍历< /a> 作为更有效查询的策略。这种行为已经内置到一些框架中,例如 Doctrine ORM,这使得它特别易于使用。
There are a few problems.
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.