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原生函數也無法解決性能問題,建議使用不同請求模式來解決了,比如實現海量無線分類使用異步請求,一級請求之后,如果要打開某個二級就異步請求數據來展示,延遲加載和分頁查詢:在處理菜單數據時,不立即加載所有子菜單項,而是在需要訪問子菜單時再進行加載??梢允褂梅猪摬樵兊姆绞剑看沃徊樵円徊糠謹祿瑴p少內存使用和查詢時間。
3,使用chatgpt來優化算法,不太靠譜
QQ一群 247823727
QQ二群 166427999
如果項目有技術瓶頸問題,請聯系↓↓
QQ: 903464207
微信: zx903464207
QQ二群 166427999
如果項目有技術瓶頸問題,請聯系↓↓
QQ: 903464207
微信: zx903464207
浙公網安備 33010602011771號