带有子菜单的渲染菜单性能严重下降

发布于 2024-12-06 13:57:47 字数 2660 浏览 0 评论 0原文

每当我渲染菜单项时,对于从数据库中找到的每个菜单项,我都会使用它来检查其子菜单。

我的控制器渲染菜单项和递归函数来检测其子菜单如下

public function renderAction()
{
    $menu = $this -> _request -> getParam('menu');
    $itemArray = $this -> getSubItems($menu);
    $container = new Zend_Navigation($itemArray);
    $this -> view -> navigation() -> setContainer($container);          
}

private function getSubItems($menu, $parent = 0) {
    $mapperMenuItem = new Apanel_Model_Mapper_MenuItem();
    $menuItems = $mapperMenuItem -> getItemsByMenu($menu, $parent);
    if(count($menuItems) > 0) {
        $itemArray = array();
        foreach($menuItems as $item) {
            $label = $item -> label;
            $uri = $this -> getSubItemUrl($item);               
            $subItems = $this -> getSubItems($menu, $item -> id);           
            if(count($subItems)) {              
                $tArray['pages'] = $subItems;
            }
            $tArray['label'] = $label;
            $tArray['uri'] = $uri;
            $itemArray[] = $tArray;
            unset($tArray);
        }
        if(count($itemArray)) {
            return $itemArray; 
        } else {
            return null;
        }       
    } else {
        return null;
    }       
}

   private function getSubItemUrl($item) {

        if(!empty($item -> link)) {
            $uri = $item -> link;                       
        } else {
            $pageMapper = new Apanel_Model_Mapper_Page();

            $details = $pageMapper -> getPageDetails($item -> page_id);             
            $pageClass = "CMS_Content_Item_".ucwords($details['namespace']);
            $page = new $pageClass($item -> page_id);
            $title = str_replace(" ", "-", strtolower($details['name']));
            $uri = $this -> view -> url(array(
                "namespace" => $details['namespace'],
                "title"     => $title
            ),'page-view');
        }
        return $uri;            
    }

,MenuItem Mapper 中的函数 getItemsByMenu

public function getItemsByMenu($menuId, $parent = 0)    {
    $select = $this -> getDbTable() -> select();        
    $select -> where("menu_id = ?", $menuId)
            -> where("parent = ?", $parent)
            -> order("position");
    $items = $this -> getDbTable() -> fetchAll($select);
    if($items -> count() > 0) {
        return $items;
    } else {
        return null;
    }
}

我在我的应用程序中渲染了大约 4 种不同类型的菜单,我注意到执行中性能显着下降。我经常遇到执行超时,使用菜单的渲染时间之间的差异约为 35 秒,而没有菜单的渲染时间约为 22 秒。这一切都在本地主机中。我的递归有什么缺陷吗?我可以采取什么措施来提高代码的性能?

Whenever I render a menu item, for each of the menu item I found from the database, I use it to check its sub-menus.

My Controller rendering the menu item and recursive function to detect its sub-menus are as follows

public function renderAction()
{
    $menu = $this -> _request -> getParam('menu');
    $itemArray = $this -> getSubItems($menu);
    $container = new Zend_Navigation($itemArray);
    $this -> view -> navigation() -> setContainer($container);          
}

private function getSubItems($menu, $parent = 0) {
    $mapperMenuItem = new Apanel_Model_Mapper_MenuItem();
    $menuItems = $mapperMenuItem -> getItemsByMenu($menu, $parent);
    if(count($menuItems) > 0) {
        $itemArray = array();
        foreach($menuItems as $item) {
            $label = $item -> label;
            $uri = $this -> getSubItemUrl($item);               
            $subItems = $this -> getSubItems($menu, $item -> id);           
            if(count($subItems)) {              
                $tArray['pages'] = $subItems;
            }
            $tArray['label'] = $label;
            $tArray['uri'] = $uri;
            $itemArray[] = $tArray;
            unset($tArray);
        }
        if(count($itemArray)) {
            return $itemArray; 
        } else {
            return null;
        }       
    } else {
        return null;
    }       
}

   private function getSubItemUrl($item) {

        if(!empty($item -> link)) {
            $uri = $item -> link;                       
        } else {
            $pageMapper = new Apanel_Model_Mapper_Page();

            $details = $pageMapper -> getPageDetails($item -> page_id);             
            $pageClass = "CMS_Content_Item_".ucwords($details['namespace']);
            $page = new $pageClass($item -> page_id);
            $title = str_replace(" ", "-", strtolower($details['name']));
            $uri = $this -> view -> url(array(
                "namespace" => $details['namespace'],
                "title"     => $title
            ),'page-view');
        }
        return $uri;            
    }

And function getItemsByMenu in MenuItem Mapper

public function getItemsByMenu($menuId, $parent = 0)    {
    $select = $this -> getDbTable() -> select();        
    $select -> where("menu_id = ?", $menuId)
            -> where("parent = ?", $parent)
            -> order("position");
    $items = $this -> getDbTable() -> fetchAll($select);
    if($items -> count() > 0) {
        return $items;
    } else {
        return null;
    }
}

I have about 4 different types of menu rendered in my app and I am noticing significant performance degradation in the execution. I often get Execution timeouts, the difference between the rendering time with the menus are about 35 sec and without are 22 sec approx. And this all in localhost. Is there any flaw in my recursion? What measure can I take to improve the performance of the code?

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

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

发布评论

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

评论(2

夜访吸血鬼 2024-12-13 13:57:47

我看不到任何可以解释 35 秒执行时间的内容,除非菜单表中有 100,000 个项目,而且根本没有索引。建议:

  1. (这是三个字段的一个索引,字段按该顺序排列。

  2. 我假设 getPageDetails code>正在做另一个数据库理想情况下,您希望在加载菜单项时加载这些详细信息(通过加入页面表),这样您就可以将页面数据数组传递给 getPageDetails 而不必这样做对每个项目执行额外的查询。

如果这没有带来任何神奇的改进,请尝试启用数据库分析器,以便您可以查看是否是数据库查询的数量或速度造成的。的问题。

I can't see anything in there that would explain 35 second execution time, unless you have 100,000s of items in your menus table with no indexes at all. Suggestions:

  1. Make sure you have an index on the menu items table on: menu_id, parent, position (that's one index on three fields, with the fields in that order.

  2. I assume getPageDetails is doing another database query. Ideally you'd want to load these details when you load the menu items (by joining in the pages table), so you could then just pass the array of page data to getPageDetails instead of having to do an additional query per item.

If that doesn't give any miraculous improvements, try enabling the DB profiler so you can see whether it's volume or speed of the database queries that is causing the issue.

菩提树下叶撕阳。 2024-12-13 13:57:47

这里明显的问题是从数据库获取菜单的方式。

如果您为每个菜单项发出一个获取其子菜单的请求,那么您很快就会收到大量请求。一个简单的解决方案是实施缓存,但您可以首先尝试改进查询菜单的方式。

映射树的一个很好的替代方法是使用物化路径,而不是引用父项。这意味着您在字段中存储一个字符串,其中包含当前项目的路径(以逗号分隔)。真正的好处是,您可以在路径字段上使用正则表达式仅在一个请求中获得一整棵树:

//get the whole tree of menu 1
SELECT * FROM menuitems WHERE path REGEXP '^1' ORDER BY path;

| ID | Name           | Path   |
| 1  | Hardware       | "1"    |
| 2  | Printers       | "1,1"  |
| 3  | Laser printers | "1,1,1"|
| 4  | Ink printers   | "1,1,2"|
| 5  | Screens        | "1,2"  |
| 6  | Flat Screens   | "1,2,1"|
| 7  | Touch Screens  | "1,2,1"|

然后使用一些代码(例如某些递归函数)构建整个导航。

并考虑缓存这类东西

The obvious problem here is the way you fetch your menu from the database.

If for each menu item you do a request to fetch it's submenus, you will quickly end with a lot of requests. An easy solution would be to implement caching, but you could first try to improve the way you query your menu.

A good alternative for mapping trees, instead of referencing the parent item is to use a materialized path. This means that you store in a field a string containing the path to the current item, comma-separated. The real benefit is that you could get a whole tree in only one request using a regexp on the path field :

//get the whole tree of menu 1
SELECT * FROM menuitems WHERE path REGEXP '^1' ORDER BY path;

| ID | Name           | Path   |
| 1  | Hardware       | "1"    |
| 2  | Printers       | "1,1"  |
| 3  | Laser printers | "1,1,1"|
| 4  | Ink printers   | "1,1,2"|
| 5  | Screens        | "1,2"  |
| 6  | Flat Screens   | "1,2,1"|
| 7  | Touch Screens  | "1,2,1"|

Then with a little code, like some recursive function you build your whole navigation.

And consider caching this sort of things

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