php优化递归算法优化

2023年8月7日13:59:31

因为最近开发自己的一些常用系统,所以为了自由度较高一点,经常分类都是无限层级,所以递归用的比较多,但是发现当分类大于三层,数据1万以上递归就会很慢,所以一直在寻求优化算法,使用使用chagpt优化的算法,基本无法使用,后续想到用php原生函数来使用,结果性能飙升

数据库结构:

CREATE TABLE `admin_permission` (
 `id` bigint unsigned NOT NULL AUTO_INCREMENT,
 `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
 `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
 `remark` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '备注',
 `is_delete` tinyint(1) NOT NULL DEFAULT '10' COMMENT '10默认99删除',
 `parent_permission_id` int unsigned NOT NULL DEFAULT '0' COMMENT '父ID 0是顶级',
 `permission_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '控制名称',
 `permission_url` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '前台控制器URL',
 `name` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '组件名称',
 `backstage_permission_url` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '后台权限URL',
 `tag` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT '' COMMENT '标签,标志',
 `is_menu` tinyint unsigned NOT NULL DEFAULT '1' COMMENT '作为菜单显示,1是,2不是',
 `small_icon_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT 'pc端图标',
 `redirect` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '跳转页面',
 `component` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT NULL COMMENT '组件类型',
 `keep_alive` tinyint NOT NULL DEFAULT '1' COMMENT '是否keep_alive',
 `hidden` tinyint(1) DEFAULT '10' COMMENT '菜单是否显示10显示20不显示',
 PRIMARY KEY (`id`),
 KEY `parent_permission_id` (`parent_permission_id`)
) ENGINE=InnoDB AUTO_INCREMENT=14384 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='管理员权限表';

生产测试数据:,随便自己插入几条数据,然后使用下面的方法在第三层生成测试数据

$list = AdminPermission::orderBy('id', 'asc')->get()->toArray();
        $array = self::treeMenu($list, 0, 'parent_permission_id');

//        pp($array);
       // 制造10000+的测试数据,测试算法姓名
        foreach ($array as $k => &$v) {
            if (!empty($v['children'])) {
                foreach ($v['children'] as $kk => &$vv) {
                    if (!empty($vv['children'])) {
                        foreach ($vv['children'] as $kkk => &$vvv) {

                            unset($vvv['label']);
                            unset($vvv['id']);
                            for ($i = 1; $i <= 100; $i++) {
                                DB::table('admin_permission')->insert($vvv);
                            }
                        }
                    }
                }
            }
        }

php实现

    //递归数据
    public static function treeMenu(array $menu = [], int $parent = 0, string $parentKey = 'parent_id')
    {
        $tree = array();
        foreach ($menu as $v) {
            if ($v[$parentKey] == $parent) {
                $v['children'] = self::treeMenu($menu, $v['id'], $parentKey);
                if (empty($v['children'])) {
                    unset($v['children']);
                }
                $tree[] = $v;
            }
        }
        return $tree;
    }

使用php原生函数实现的,默认三层搜索

$tree = array_filter($list, function ($permission) use ($parent_id, $parentKey) {
            return $permission[$parentKey] == $parent_id;
        });
        if (!empty($tree)) {
            foreach ($tree as &$v) {

                $parent_id = $v['id'];
                $v['children'] = array_filter($list, function ($permission) use ($parent_id, $parentKey) {
                    return $permission[$parentKey] == $parent_id;
                });

                if (!empty($v['children'])) {
                    foreach ($v['children'] as &$vv) {

                        $parent_id = $vv['id'];
                        $vv['children'] = array_filter($list, function ($permission) use ($parent_id, $parentKey) {
                            return $permission[$parentKey] == $parent_id;
                        });

//                        if (!empty($vv['children'])) {
//                            foreach ($vv['children'] as &$vvv) {
//
//                                $parent_id = $vvv['id'];
//                                $vvv['children'] = array_filter($list, function ($permission) use ($parent_id, $parentKey) {
//                                    return $permission[$parentKey] == $parent_id;
//                                });
//                            }
//                        }
                    }
                }
            }
        }

测试:

1,php实现方法测试:

第一次:
待递归总数--:14350
开始时间:2023-08-08 10:28:22
结束时间:2023-08-08 10:28:28

第二次:
待递归总数--:14350
开始时间:2023-08-08 10:29:04
结束时间:2023-08-08 10:29:09

2,php原生实现方法测试:

第一次:
待递归总数--:14350
开始时间:2023-08-08 10:30:46
结束时间:2023-08-08 10:30:46

第二次:
待递归总数--:14350
开始时间:2023-08-08 10:31:00
结束时间:2023-08-08 10:31:01

完整代码:

public function index(Request $request)
    {
        $list = AdminPermission::orderBy('id', 'asc')->get()->toArray();
        $array = self::treeMenu($list, 0, 'parent_permission_id');

//        pp($array);
       // 制造10000+的测试数据,测试算法姓名
        foreach ($array as $k => &$v) {
            if (!empty($v['children'])) {
                foreach ($v['children'] as $kk => &$vv) {
                    if (!empty($vv['children'])) {
                        foreach ($vv['children'] as $kkk => &$vvv) {

                            unset($vvv['label']);
                            unset($vvv['id']);
                            for ($i = 1; $i <= 100; $i++) {
                                DB::table('admin_permission')->insert($vvv);
                            }
                        }
                    }
                }
            }
        }
//die;

        $count = AdminPermission::orderBy('id', 'asc')->get()->count();
        p('待递归总数--:' . $count);

        $list = AdminPermission::orderBy('id', 'asc')->get(['id', 'parent_permission_id', 'permission_name'])->toArray();

        //php代码实现递归
        p('开始时间:' . date('Y-m-d H:i:s'));
        $rr = self::treeMenu($list, 0, 'parent_permission_id');
        p('结束时间:' . date('Y-m-d H:i:s'));

        //php原生函数实现递归 
        p('开始时间:' . date('Y-m-d H:i:s'));
        $parent_id = 0;
        $parentKey = 'parent_permission_id';

        $tree = array_filter($list, function ($permission) use ($parent_id, $parentKey) {
            return $permission[$parentKey] == $parent_id;
        });
        if (!empty($tree)) {
            foreach ($tree as &$v) {

                $parent_id = $v['id'];
                $v['children'] = array_filter($list, function ($permission) use ($parent_id, $parentKey) {
                    return $permission[$parentKey] == $parent_id;
                });

                if (!empty($v['children'])) {
                    foreach ($v['children'] as &$vv) {

                        $parent_id = $vv['id'];
                        $vv['children'] = array_filter($list, function ($permission) use ($parent_id, $parentKey) {
                            return $permission[$parentKey] == $parent_id;
                        });

//                        if (!empty($vv['children'])) {
//                            foreach ($vv['children'] as &$vvv) {
//
//                                $parent_id = $vvv['id'];
//                                $vvv['children'] = array_filter($list, function ($permission) use ($parent_id, $parentKey) {
//                                    return $permission[$parentKey] == $parent_id;
//                                });
//                            }
//                        }
                    }
                }
            }
        }
        p('结束时间:' . date('Y-m-d H:i:s'));
        pp($tree);
    }

    //递归数据
    public static function treeMenu(array $menu = [], int $parent = 0, string $parentKey = 'parent_id')
    {
        $tree = array();
        foreach ($menu as $v) {
            if ($v[$parentKey] == $parent) {
                $v['children'] = self::treeMenu($menu, $v['id'], $parentKey);
                if (empty($v['children'])) {
                    unset($v['children']);
                }
                $tree[] = $v;
            }
        }
        return $tree;
    }

总结

1,如果使用php实现某些算法性能不好的时候,一定要使用php原生函数试试
2,如果使用php原生函数也无法解决性能问题,建议使用不同请求模式来解决了,比如实现海量无线分类使用异步请求,一级请求之后,如果要打开某个二级就异步请求数据来展示,延迟加载和分页查询:在处理菜单数据时,不立即加载所有子菜单项,而是在需要访问子菜单时再进行加载。可以使用分页查询的方式,每次只查询一部分数据,减少内存使用和查询时间。
3,使用chatgpt来优化算法,不太靠谱

posted on 2023-08-08 10:37  zh7314  阅读(83)  评论(0编辑  收藏  举报