Subsonic使用ActiveRecord的linq查询速度与SimpleRepository相比非常慢

3

有人知道为什么使用Active Record查询时,linq查询速度比使用SimpleRepository慢6倍吗?以下代码在循环中执行1000次,比使用简单存储库查询数据时慢了6倍。

先行致谢。

        string ret = "";            
//      if (plan == null)
        {
           plan =VOUCHER_PLAN.SingleOrDefault(x => x.TENDER_TYPE == tenderType);
        }
        if (plan == null)
           throw new InvalidOperationException("voucher type does not exist." + tenderType);

        seq = plan.VOUCHER_SEQUENCES.First();
        int i = seq.CURRENT_NUMBER;
        seq.CURRENT_NUMBER += seq.STEP;
        seq.Save();

你对 ActiveRecord/SimpleRepository 执行的 SQL 进行过比较吗? - Adam Cooper
你做了亚当建议的事吗?如果你找到答案,我很想知道。 - Jon
有趣的是,我已经对 SQL 进行了分析,没有任何调用需要超过几毫秒。似乎瓶颈在应用程序中:/ - aboutme
你是否注意到你的 VOUCHER_PLAN 构造函数被多次调用了?你可能在里面有某种 ToList() 操作,实例化了所有的值,但我不知道如何避免这种情况,除非尝试使用 SimpleRepository。 - nvuono
我遇到了同样的问题,它让我抓狂。获取一个包含350条记录的列表,查询/传输时间甚至无法测量。然而,由于某种未知原因,创建这些对象仍然需要超过2秒钟的时间。 - Andrew
在一个测试应用程序中:Linq2Sql: 约0.11秒,SubSonic:约1.1秒。 在所有情况下,subsonic在这个数据集上仍然比linq2sql慢10倍。2秒是我误读了,所有其他太慢的查询都是耗时1秒,这是特定的1秒查询。 - Andrew
3个回答

2
我们对此进行了一些分析,发现SubSonic的record.SingleOrDefault(x=>x.id=someval)比通过CodingHorror执行相同查询慢了多达20倍。我们在这里记录了它:https://github.com/subsonic/SubSonic-3.0/issues/258
分析器指向了ExecutionBuilder.cs中的这个问题。
// this sucks, but since we don't track true SQL types through the query, and ADO throws exception if you
// call the wrong accessor, the best we can do is call GetValue and Convert.ChangeType
Expression value = Expression.Convert(
    Expression.Call(typeof (Convert), "ChangeType", null,
                    Expression.Call(reader, "GetValue", null, Expression.Constant(iOrdinal)),
                    Expression.Constant(TypeHelper.GetNonNullableType(column.Type), typeof(Type))
        ),
    column.Type
    );

很失望,因为我真的很喜欢SubSonic/Linq。

最终我们放弃了,我写了这个 - http://www.toptensoftware.com/petapoco。在移植后,我们的负载测试显示每秒请求增加,CPU负载从大约80%降至5%。


请查看我所做的这些测试:https://dev59.com/8UzSa4cB1Zd3GeqPlU3s - Aristos

0

通过在构造函数/初始化过程中缓存数据库实例,我能够在性能方面取得巨大的差异。现在我看到的是大约2-3倍的加速,具体取决于情况和运行。

1) 如果您只调用默认构造函数,则仅使用静态实例替换_db的方法就可以正常工作,并具有相同的速度优势。

// MyProject.MyDB _db;
// replace with a static instance, and remove the "this." from other lines
static MyProject.MyDB _db = new MyDB();

public MyClass() {
    //_db=new MyProject.MyDB();
    Init();
}

2) 我编写了一个小的缓存类来缓存数据库条目,并从我的ActiveRecord.tt文件中调用它,以替换所有旧的使用"new()"的地方。

// REPLACE "MyDB" with the name of your DB.  Alternately, include this 
// class in Context.tt and have it generate the correct name.  

class ContextDatabaseCache {        

    public static MyDB GetMyDB()
    {
        return GetInstance("~~default~~", () => new MyDB());
    }

    public static MyDB GetMyDB(string connectionString) {
        return GetInstance(connectionString, () => new MyDB(connectionString));
    }

    public static MyDB GetMyDB(string connectionString, string providerName)
    {
        return GetInstance(connectionString + providerName, () => new MyDB(connectionString, providerName));
    }

    private static Dictionary<string, MyDB> _dict = new Dictionary<string, MyDB>();
    private static MyDB GetInstance(string key, Func<MyDB> createInstance)
    {
        if (!_dict.ContainsKey(key)) {               
            lock (_dict) {
                if (!_dict.ContainsKey(key)) {
                    _dict.Add(key, createInstance());
                }
            }
        }
        return _dict[key];
    }

    ///<summary>Call this when the "DefaultConnection" string changes in the
    ///         App.exe.config file so that a new db instance will be created
    ///         and pick up the changed value. </summary>
    public static void Clear() {
         _dict.Clear();
    }

}

这是在ActiveRecord.tt文件中进行的替换类型:

public <#=tbl.ClassName#>(){
    _db=new <#=Namespace#>.<#=DatabaseName#>DB();
    Init();            
}

    // becomes this: 
public <#=tbl.ClassName#>(){
    _db= <#=Namespace#>.ContextDatabaseCache.Get<#=DatabaseName#>DB();
    Init();            
}

0

显然,这在subsonic中“不是问题”,尽管他们知道它的存在。它将不会被修复。你必须使用一个糟糕的批量查询语法来解决这个问题,但没有人会这样做。

我不理解的是,这是90%的情况。从表中获取记录列表。它应该是快速的,而不是慢的。每个人都这样做,无论何时何地。

Subsonic有很多问题。我不得不为DB字段=>对象字段查找编写缓存,因为它们太慢了。


嗨,安德鲁,你说得对。在使用1.0.3时,我非常喜欢Subsonic。它可以加快开发和执行速度。但是当我们将其移植到3.0时,我遇到了很多与速度相关的问题。我们现在正在尝试2.1,因为我们无法完全更改Subsonic。Linq查询的执行时间比以前长一倍。感谢您发布有关数据库缓存的帖子。您能否为我们提供DB字段=>对象字段查找的代码?这将非常有帮助。(电子邮件地址已删除)谢谢 - Manish Pansiniya
抱歉,我回顾了我所有的代码,但我没有看到针对subsonic的.TT更改。那是以前的工作,所以我可能没有带走它们。我认为它只是遵循了正常的“找到它执行的位置,编写一个简单的基于哈希映射的缓存,然后完成”的模式,就像这里一样。 - Andrew

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接