【问题标题】:Subsonic linq using activerecord very slow compared to simplerepository与 simplerepository 相比,使用 activerecord 的 Subsonic linq 非常慢
【发布时间】:2011-05-11 21:52:33
【问题描述】:

任何人都知道为什么使用活动记录与 simplerepository 进行查询时 linq 查询的速度要慢 6 倍? 下面的代码运行速度比我使用简单存储库查询数据时慢 6 倍。这段代码循环执行1000次

提前致谢

        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
  • 你按照亚当的建议做了吗?如果您找到答案,我很想知道。
  • 有趣的是,我对 SQL 进行了分析,没有一个调用需要超过几毫秒的时间。它似乎在应用程序的某个地方遇到了瓶颈:/
  • 您是否注意到您的 VOUCHER_PLAN 构造函数被调用了很多次?您可能会在其中进行某种 ToList() 实例化所有值,但除了尝试 SimpleRepository 之外,我真的不知道如何避免它。
  • 我有同样的问题,这让我发疯。获取 350 条记录的列表和查询/otw 时间甚至无法衡量。但是,由于未知原因,创建对象仍然需要 2 多秒。

标签: subsonic performance


【解决方案1】:

我们对此进行了一些分析,发现 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%。

【讨论】:

【解决方案2】:

通过缓存它在构造函数/初始化过程中创建的数据库实例,我能够在性能上产生巨大的差异。我现在看到的是 ~2-3x 加速,具体取决于情况和运行。

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) 我已经为 DB 条目编写了一个小缓存类,并从我的 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();            
}

【讨论】:

    【解决方案3】:

    显然,这对亚音速“不是问题”,尽管他们知道它存在。它不会被修复。您必须使用蹩脚的批处理查询语法来获得它,而没有人会这样做。

    我不明白的是,这是 90% 的情况。从表中获取记录列表。它应该成为快的,而不是慢的。每个人、任何地方、任何时候都这样做。

    亚音速的问题太多了。我不得不为 DB 字段 => 对象字段查找编写缓存,因为它们也太慢了。

    【讨论】:

    • 嗨安德鲁,你是对的。使用 1.0.3 时,我非常喜欢亚音速。加快开发和执行速度。但是当我们将它移植到 3.0 时,我在速度方面遇到了很多麻烦。我们现在正在尝试 2.1,因为我们无法完全改变亚音速。 Linq 查询的执行时间是以前的两倍。感谢您发布数据库缓存。您能否向我们提供 DB 字段 => 对象字段查找的代码。这会有很大帮助。 (电子邮件地址已删除)谢谢
    • 抱歉,我查看了我拥有的所有代码,但我没有看到亚音速的 .TT 更改。这是以前的工作,所以我可能不会带走它们。我认为它只是遵循正常的“找到它的位置,编写一个简单的基于 hashmap 的缓存,然后完成它”的模式,就像这里。
    猜你喜欢
    • 1970-01-01
    • 2021-02-13
    • 2017-05-07
    • 1970-01-01
    • 2017-06-05
    • 2015-01-27
    • 2014-09-06
    • 1970-01-01
    • 2012-09-05
    相关资源
    最近更新 更多