You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何限制PHP递归函数?解决树形递归内存溢出问题

解决递归加载树形结构导致浏览器内存耗尽的问题

我用递归函数生成流程图时,因为会遍历树形结构的所有子节点,导致浏览器内存耗尽,想找有效的递归限制方法。

原递归代码

if(mysqli_num_rows($rq)>0) {
    $kid = mysqli_fetch_assoc($rq);
    $sq = "SELECT groupe_id FROM groupe_tbl WHERE groupe_groupe_id=$groupe_id";
    $rq = IPT_query($sq);
    
    if(mysqli_num_rows($rq)>0){
        $kid['kids'] = array();
        while($k=mysqli_fetch_assoc($rq)) {
            $kid['kids'][] = get_kids($k['groupe_id']);
        }
    } else $kid['kids'] = false;
    return $kid;
}

已尝试但无效的方法

我试过限制单层级的子节点数量,但这只是减少了当前层的节点数,并没有从根本上解决递归遍历所有层级导致的内存问题:

while($i<20 and $k=mysqli_fetch_assoc($rq)) {
    $kid['kids'][] = get_kids($k['groupe_id']); $i++;
}

可行的解决方案

1. 限制递归深度

给递归函数增加深度参数,设定最大允许遍历的层级,超过后停止加载子节点,从根源上减少节点总数。

function get_kids($groupe_id, $current_depth = 0, $max_depth = 3) {
    // 获取当前节点信息
    $sq_node = "SELECT * FROM groupe_tbl WHERE groupe_id = $groupe_id";
    $rq_node = IPT_query($sq_node);
    $kid = mysqli_fetch_assoc($rq_node);

    // 超过最大深度则停止加载子节点
    if ($current_depth >= $max_depth) {
        $kid['kids'] = false;
        return $kid;
    }

    // 查询子节点
    $sq_kids = "SELECT groupe_id FROM groupe_tbl WHERE groupe_groupe_id = $groupe_id";
    $rq_kids = IPT_query($sq_kids);
    
    if(mysqli_num_rows($rq_kids) > 0){
        $kid['kids'] = array();
        while($k = mysqli_fetch_assoc($rq_kids)) {
            // 递归调用时深度+1
            $kid['kids'][] = get_kids($k['groupe_id'], $current_depth + 1, $max_depth);
        }
    } else {
        $kid['kids'] = false;
    }
    return $kid;
}

2. 用迭代替代递归

递归会占用大量调用栈内存,改用栈/队列实现迭代遍历,能更灵活控制节点加载,避免栈溢出和内存过载。

function get_kids_iterative($root_groupe_id, $max_depth = 3) {
    // 栈存储待处理节点:包含节点ID、当前深度、父节点ID
    $stack = [[
        'id' => $root_groupe_id,
        'depth' => 0,
        'parent_id' => null
    ]];
    // 映射表存储已处理的节点,方便关联父节点
    $node_map = [];
    $root_node = null;

    while (!empty($stack)) {
        $current = array_pop($stack);
        $groupe_id = $current['id'];
        $depth = $current['depth'];

        // 查询当前节点信息
        $sq_node = "SELECT * FROM groupe_tbl WHERE groupe_id = $groupe_id";
        $rq_node = IPT_query($sq_node);
        $node = mysqli_fetch_assoc($rq_node);
        $node['kids'] = [];

        // 未超过最大深度则加载子节点
        if ($depth < $max_depth) {
            $sq_kids = "SELECT groupe_id FROM groupe_tbl WHERE groupe_groupe_id = $groupe_id";
            $rq_kids = IPT_query($sq_kids);
            while ($child = mysqli_fetch_assoc($rq_kids)) {
                $child_id = $child['groupe_id'];
                // 将子节点加入栈等待处理
                array_push($stack, [
                    'id' => $child_id,
                    'depth' => $depth + 1,
                    'parent_id' => $groupe_id
                ]);
                $node['kids'][] = $child_id;
            }
        }

        // 存入映射表
        $node_map[$groupe_id] = $node;

        // 关联到父节点的子列表
        if ($current['parent_id'] === null) {
            $root_node = $node;
        } else {
            $parent_node = &$node_map[$current['parent_id']];
            foreach ($parent_node['kids'] as &$kid_id) {
                if ($kid_id === $groupe_id) {
                    $kid_id = $node;
                    break;
                }
            }
        }
    }

    return $root_node;
}

3. 前端懒加载

后端只返回第一层节点,前端点击节点时再请求该节点的直接子节点,避免一次性加载全量数据。后端接口示例:

// 获取指定节点的直接子节点(不递归)
function get_direct_kids($groupe_id) {
    $sq = "SELECT * FROM groupe_tbl WHERE groupe_groupe_id = $groupe_id";
    $rq = IPT_query($sq);
    $kids = [];
    while ($row = mysqli_fetch_assoc($rq)) {
        $row['has_kids'] = false;
        // 判断是否有子节点,给前端显示展开按钮
        $sq_check = "SELECT 1 FROM groupe_tbl WHERE groupe_groupe_id = {$row['groupe_id']} LIMIT 1";
        $rq_check = IPT_query($sq_check);
        if (mysqli_num_rows($rq_check) > 0) {
            $row['has_kids'] = true;
        }
        $kids[] = $row;
    }
    return json_encode($kids);
}

4. 限制总加载节点数

在递归过程中维护一个计数器,当总节点数达到设定值时,停止加载新的子节点:

function get_kids_with_limit($groupe_id, &$total_count = 0, $max_count = 50) {
    if ($total_count >= $max_count) {
        return null;
    }

    $sq_node = "SELECT * FROM groupe_tbl WHERE groupe_id = $groupe_id";
    $rq_node = IPT_query($sq_node);
    $kid = mysqli_fetch_assoc($rq_node);
    $total_count++;

    $sq_kids = "SELECT groupe_id FROM groupe_tbl WHERE groupe_groupe_id = $groupe_id";
    $rq_kids = IPT_query($sq_kids);
    $kid['kids'] = [];

    while ($k = mysqli_fetch_assoc($rq_kids)) {
        if ($total_count >= $max_count) {
            break;
        }
        $child = get_kids_with_limit($k['groupe_id'], $total_count, $max_count);
        if ($child) {
            $kid['kids'][] = $child;
        }
    }

    if (empty($kid['kids'])) {
        $kid['kids'] = false;
    }
    return $kid;
}

内容的提问来源于stack exchange,提问作者Hosni ISMAIL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 12:55:18