• EF4 内存/效能改善一案


    本文所要分享的内容在特定的背景下,请予以注意。

    补充:有朋友回复不明本文在分享什么,这里给予补充说明
    大致的情况是这样的,有数百个相同架构的 DB 分配给不同的客户使用。然而他们共享一套高层的逻辑组件,这些组件需要在某些情况下操作所有的这些 DB (如提取某些资料后发送邮件等)。这样导致了链接发生变化,实例化 ObjectContext 并及时的释放分配的资源,但 释放分配资源 这个动作并没有达到预期的效果,内存一直被吃着,效率一直下降着。文末的代码有例子。为了解决这个问题,搜索了不少网上的资料(都是在提醒 using 和 Detach 或是 ToList),但这不是合适的解决方案。在经过一番排查后发现 EF6 不存在这个问题,效率很高,内存也不会持续增长,查看代码得知 EF4 随着链接的不同会实例化多个 MetadataWorkspace 而不去思考 Metadata 的相同性(EF6 不会)。最后文末给出了一个 BuildFromCache 的方法来解决这个问题。

    背景:使用 EF4 框架,操纵多个拥有同样架构的 DB

    问题:发现随着 DB 数量的增加其内存几乎耗尽,查询效率极其低下。

    过程:

    1. 接到这个问题开始便大量 Google 关键词: EF Memory Leaks | EF Dispose 等,发现有不少类似的问题但都木有一个可行的答案,也没有解决这个问题。而后决定使用工具分析。
    2. 使用  ANTS Memory Profiler 7(福利进行内存分析,知道了哪些对象在吃着内存。
    3. Google 了一番那些吃内存对象的关键词,也有相当多的篇幅在咨询类似的问题,如: MetadataWorkspace | EntityConnection等。
    4. 尝试使用了一下 EF6,这个问题居然不存在了-_- !,尼玛看代码吧。
    5. EntityConnection中提供的 GetMetadataWorkspace 映入眼帘,就是它了,因为 EF4 它爱上了 new

    解法:EntityConnection 实例化的时候始终共享工作区(下面的范例代码中的 BuildFromCache(string catalog) 方法)。

    重现:

    • 制造背景:
    View Code
    DECLARE @num INT 
    DECLARE @max INT 
    DECLARE @catalog NVARCHAR(16)
    DECLARE @dbFullName NVARCHAR(200)
    DECLARE @logFullName NVARCHAR(200)
    SET @num = 0
    SET @max = 1 
    WHILE @num < @max 
        BEGIN
            SET @catalog = 'DB' + REPLACE(STR(@num, 3), SPACE(1), '0')
            
            SET @dbFullName = N'D:\Database\' + @catalog + N'.mdf'
            SET @logFullName = N'D:\Database\' + @catalog + N'.LDF'
            
            RESTORE DATABASE @catalog
            FROM  DISK = N'D:\Database\Backup\AdventureWorks2008R2.bak' 
            WITH  FILE = 1,  
            MOVE N'AdventureWorks2008R2_Data' TO @dbFullName,  
            MOVE N'AdventureWorks2008R2_Log' TO @logFullName,  
            NOUNLOAD,  
            STATS = 10
            
            SET @num = @num + 1 
        END
    • 测试代码:
    View Code
    using System;
    using System.Collections.Generic;
    using System.Data.EntityClient;
    using System.Data.Metadata.Edm;
    using System.Data.SqlClient;
    using System.Diagnostics;
    using System.Globalization;
    using System.Linq;
    
    namespace Foo
    {
        internal class Program
        {
            private static void Main(string[] args)
            {
                Stopwatch watch = Stopwatch.StartNew();
                IEnumerable<string> catalogs = GenerateCatalogs();
                foreach (string catalog in catalogs)
                {
                    //using (AdventureWorksModelContainer container = ContainerBuilder.Build(catalog))
                    using (AdventureWorksModelContainer container = ContainerBuilder.BuildFromCache(catalog))
                    {
                        Employee employee = container.Employee.First(o => true);
                    }
                }
                watch.Stop();
                Console.WriteLine(watch.Elapsed);
                Console.ReadKey();
            }
    
            private static IEnumerable<string> GenerateCatalogs()
            {
                var names = new string[20];
                for (int i = 1; i <= names.Length; i++)
                    yield return string.Concat("DB", i.ToString(CultureInfo.InvariantCulture).PadLeft(3, '0'));
            }
        }
    
        internal class ContainerBuilder
        {
            private const string Metadata = "res://*/AdventureWorksModel.csdl|res://*/AdventureWorksModel.ssdl|res://*/AdventureWorksModel.msl";
            private const string Connection = "data source=.;initial catalog={0};integrated security=True;MultipleActiveResultSets=True";
    
            private static MetadataWorkspace _metadataWorkspace;
    
            public static AdventureWorksModelContainer Build(string catalog)
            {
                string esc = new EntityConnectionStringBuilder
                    {
                        Provider = "System.Data.SqlClient",
                        ProviderConnectionString = string.Format(Connection, catalog),
                        Metadata = Metadata
                    }.ToString();
                return new AdventureWorksModelContainer(esc);
            }
    
            public static AdventureWorksModelContainer BuildFromCache(string catalog)
            {
                string connection = string.Format(Connection, catalog);
                if (_metadataWorkspace == null)
                {
                    string esc = new EntityConnectionStringBuilder
                        {
                            Provider = "System.Data.SqlClient",
                            ProviderConnectionString = connection,
                            Metadata = Metadata
                        }.ToString();
                    using (var ec = new EntityConnection(esc))
                    {
                        _metadataWorkspace = ec.GetMetadataWorkspace();
                    }
                }
                return new AdventureWorksModelContainer(new EntityConnection(_metadataWorkspace, new SqlConnection(connection)));
            }
        }
    }
  • 相关阅读:
    nodeJS操作mongoDB常用api
    git命令
    Scala笔记(1)——基本知识
    Akka Java 中文文档
    训练:用gfsh执行常见的任务
    https://confluence.jnj.com/display/AAIM/Enterprise+Agile+Data+Services
    gfsh操作
    java知识
    maven使用之烦人的.lastUpdated文件
    idea相关操作
  • 原文地址:https://www.cnblogs.com/le618n/p/3041410.html
Copyright © 2020-2023  润新知