具有子菜单的大量性能降级渲染菜单

时间:2011-09-26 16:06:45

标签: zend-framework menu profiling execution

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

My Controller渲染菜单项和递归函数以检测其子菜单如下

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秒。这一切都在localhost中。我的递归有什么缺陷吗?我可以采取什么措施来提高代码的性能?

2 个答案:

答案 0 :(得分:0)

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

  1. 确保您在菜单项目表上有一个索引:menu_id, parent, position(这是三个字段的一个索引,字段按此顺序排列。

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

  3. 如果这没有带来任何奇迹般的改进,请尝试启用数据库分析器,以便您可以查看导致问题的数据库查询的数量或速度。

答案 1 :(得分:0)

这里显而易见的问题是你从数据库中获取菜单的方式。

如果对于每个菜单项您都要求获取它的子菜单,您将很快以很多的请求结束。一个简单的解决方案是实现缓存,但您可以首先尝试改进查询菜单的方式。

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

//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"|

然后使用一些代码,就像一些递归函数,你可以构建整个导航。

并考虑缓存此类事情