【问题标题】:At a glance can anyone tell me why this Action is so slow?一目了然,谁能告诉我为什么这个动作这么慢?
【发布时间】:2011-04-13 19:02:28
【问题描述】:

这个东西一开始并没有注意到它很慢,因为数据库中的“树节点”并不多。现在真的很慢,一看这有什么大问题吗?我真的需要对其进行优化,在我重新设计整个事情之前,我想知道是否有什么突出是真正的痛点。我已经将慢速部分缩小到递归存储库函数,这是这篇文章的最后一件事,但我必须定义一些导致它发生的事情......祝你好运。 (注意:这不是我写的,我只是进行了损坏控制,我正在努力解决它。)

首先要了解几点:

JsTreeNode 定义:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace App.Models
{
    public class JsTreeNode
    {
        public JsTreeNode()
        {
        }

        public Attributes attributes { get; set; }
        public Data data { get; set; }
        public string state { get; set; }
        public List<JsTreeNode> children { get; set; }
    }

    public class Attributes
    {
        public string id { get; set; }
        public string rel { get; set; }
        public string mdata { get; set; }
        public string href { get; set; }
        public string complete { get; set; }
        public string edit { get; set; }
        public string title { get; set; }
        public string resourceAccountID { get; set; }
    }

    public class Data
    {
        public string title {get;set;}
        public string icon {get;set;}
    } 
}

现在是 TreeNodes 表定义:

CREATE TABLE [dbo].[TreeNodes](
    [TreeNodeID] [int] IDENTITY(1,1) NOT NULL,
    [TreeID] [int] NOT NULL,
    [ParentNodeID] [int] NULL,
    [ResourceID] [int] NULL,
    [NodeOrder] [int] NOT NULL,
    [NodeName] [nvarchar](250) NOT NULL,
    [CreateBy] [int] NULL,
    [CreateDate] [datetime] NULL,
    [ModifyBy] [int] NULL,
    [ModifyDate] [datetime] NULL,
    [TreeRevisionID] [int] NULL,

树修订表定义...

CREATE TABLE [dbo].[TreeRevisions](
    [TreeRevisionID] [int] IDENTITY(1,1) NOT NULL,
    [TreeID] [int] NOT NULL,
    [Notes] [text] NULL,
    [CreatedBy] [int] NOT NULL,
    [CreatedDate] [datetime] NOT NULL,
    [ModifyBy] [int] NOT NULL,
    [ModifyDate] [datetime] NOT NULL,

现在这里是 Get Action 本身,它需要 20-25 秒,您可以在那里看到它调用了我在此线程中进一步定义的存储库函数。

public ContentResult Get(string target,int id)
        {
            int revisionID = Convert.ToInt32(Request.QueryString["revisionID"]);
            int tempUserID = (Request.QueryString["userID"] != null)
                                 ? Convert.ToInt32(Request.QueryString["userID"])
                                 : UserID;

            var nodesList = new List<JsTreeNode>();
            if(target.Contains("tree"))
            {
                tree tree = _treeRepository.GettreeByID(id);

                var cnode = new JsTreeNode
                                       {
                                           attributes = new Attributes {id = "0",title = tree.treeName},
                                           data = new Data {title = tree.treeName},
                                           children = _treeNodesRepository.GetNodesBytreeID(id, null,revisionID),
                                           state = "open"
                                       };
                cnode.attributes.rel = "root";
                cnode.attributes.mdata = "{draggable : true}";
                nodesList.Add(cnode);
            }
            else
            {

                var trees = _CategoryRepository.getAlltreesByCategoryID(id);

                IQueryable<tree> custom;

                if(revisionID != 0)
                {
                    custom = from c in trees
                              where
                                  c.AccountID == AccountID
                              select c;
                } else
                {
                    custom = from c in trees
                             where
                                 c.AccountID == AccountID && c.PublishedRevisionID != null && c.PublishedRevisionID != 0
                             select c;
                }

                var acme = from c in trees where c.AccountID == acmeContent.acmeID select c;

                foreach (var tree in (custom.Count() > 0) ? custom : acme)
                {
                    if(revisionID == 0 && tree.PublishedRevisionID == null) continue;
                    int tempRev = (revisionID != 0) ? revisionID : (int)tree.PublishedRevisionID;

                    if(custom.Count() == 1)
                    {
                        var tempNodes = _treeNodesRepository.GetNodesBytreeID(tree.treeID, null, tempUserID, tempRev);
                        nodesList.AddRange(tempNodes);
                    }
                    else
                    {
                        var cnode = new JsTreeNode();
                        cnode.attributes = new Attributes { id = tree.treeID.ToString(), title = tree.treeName };
                        cnode.data = new Data { title = tree.treeName };
                        cnode.children = _treeNodesRepository.GetNodesBytreeID(tree.treeID, null, tempUserID, tempRev);
                        cnode.attributes.rel = "Folder";

                        nodesList.Add(cnode);
                    }
                }
            }

            var ser = new JavaScriptSerializer();
            string res = ser.Serialize(nodesList);

            return Content(res,"application/json");

        }

...最后,“罪魁祸首”,看看它是如何称呼自己的:

public List<JsTreeNode> GetNodesByTreeID(int TreeID, int? parentID,int userID, int revisionID)
        {

            IQueryable<UserTreeNode> TreeNodes = GetAllTreeNodesByTreeIDAndParentNodeID(TreeID, parentID,userID,revisionID);
            List<JsTreeNode> nodesList = new List<JsTreeNode>();



            foreach (UserTreeNode node in TreeNodes)
            {
                string nodeName = node.Node.NodeName.Replace("'", "&#39;");

                JsTreeNode cnode = new JsTreeNode
                                       {
                                           attributes = new Attributes
                                                            {id = node.Node.TreeNodeID.ToString(),
                                                            title = nodeName},
                                           data = new Data {title = nodeName}
                                       };

                if(node.Node.ResourceID != null)
                {
                    cnode.attributes.complete = (node.IsComplete) ? "true" : "false";
                    cnode.attributes.rel = ResourceTypes.ReturnResourceTypeName(_resourceRepository.getResourceByID(Convert.ToInt32(node.Node.ResourceID)).ResourceTypeID);
                    cnode.attributes.href = "/resource/" + node.Node.ResourceID + "?minimal=true&nodeID=" + node.Node.TreeNodeID.ToString();
                }
                else
                {
                    var nodeChildren = GetNodesByTreeID(TreeID, node.Node.TreeNodeID,userID,revisionID);
                    if (nodeChildren.Count > 0)
                        cnode.children = nodeChildren;

                    cnode.attributes.complete = "false";
                    cnode.attributes.rel = "Folder";
                }

                nodesList.Add(cnode);
            }

            return nodesList;
        }

【问题讨论】:

  • omg 自动属性,认真的。
  • 请详细说明,这是什么意思?我没有写这个,我只是被放在了损害控制上。 :)
  • 不是答案,但是您是否使用 Profiler 来观察您的查询命中数据库?我敢打赌,您要么执行大量单独的查询,要么正在拖下大量数据。树数据结构可能会在短时间内变得非常庞大。
  • 我怎样才能做 Profiler 的事情,也许可以给我链接一个教程?
  • @Shogun 自动属性:public string OMG {get;set;} 将其与您发布的代码中的任何单个属性进行比较。每个属性会使您的来源减少大约九行。

标签: c# asp.net-mvc linq-to-sql json recursion


【解决方案1】:

您发布的表包含一个标识列,但这些列未指定为主键(至少在您显示的示例中没有)。

我找不到您的 LinqToSql 查询结构,因此很难评论查询的优化。我怀疑它都包含在:GetAllTreeNodesByTreeIDAndParentNodeID

在 Sql Studio 中单独运行这些语句:

sp_help TreeNodes
sp_help TreeRevisions

第 6 个结果集是索引列表...在您的情况下 - 此结果集可能为空。如果您没有索引,那是您的第一个问题。

一目了然,我建议添加这些索引

TreeNodes
  TreeNodeID primary key
x TreeID nonclustered index
  TreeRevisionID nonclustered index
* TreeID, ParentNodeId nonclustered index

TreeRevisions
  TreeRevisionID primary key
  TreeID nonclustered index

标有 * 的那一项可能是您当前查询方式中最重要的一项。列顺序在多列索引中很重要。

还可以考虑通过 TreeId 获取整棵树,并在内存中进行更多过滤/整形。这将避免在数据库中进行递归/重复查询。使用这种方法,标有 x 的索引是重要索引。

【讨论】:

    【解决方案2】:

    在此过程中,我最好的建议是启动Visual Studio Profiler(与 Sql Profiler 不同)并了解该过程的哪个部分实际上花费了最多的时间。

    如果您没有带有分析器的 VS 版本,您可以 check out this search 获取其他应用程序分析器。首先是针对 asp.net。

    【讨论】:

    • 我也在想,也许不是每次请求这棵树时都构建这个 JSON 内容,我可以在创建树时构建它,并在请求树时将其保存在数据库中。
    猜你喜欢
    • 2017-05-03
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 2013-05-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多