簡體   English   中英

由於 InitializedDatabases 列表,EntityFramework6 內存使用量很大

[英]EntityFramework6 memory usage with large amount of table due to InitializedDatabases list

在我們的應用程序中有大量的表(大約 50k)——所有這些表都被實際使用,這導致實體框架中的內存消耗很高。

在進行了一些內存分析之后,我注意到 DbCompiledModel 類被保存在內存中,所以經過一些搜索后,將其追蹤到保留“InitializedDatabases”列表的 LazyInternalContext 類。

https://github.com/dotnet/ef6/blob/master/src/EntityFramework/Internal/LazyInternalContext.cs#L670

有沒有辦法阻止實體框架這樣做?,如果這是“InitializeDatabaseAction”所暗示的,它不是代碼優先設置,數據庫設置和遷移不在此應用程序中完成。

設置“返回空值”或將“InitializerDisabled”設置為 true 會使一切正常,但寧願不運行自定義實體構建,而且不知道僅“更改”源會產生什么影響。

大多數表都有相同的定義,所以也嘗試了我在這里找到的解決方案: 在運行時更改表名

嘗試此操作時,我收到錯誤消息“此命令存在開放數據讀取器”,不支持使用 postgres 和 MARS(不知道為什么我需要它,這只會更改正在運行的 sql)

解決方案是在評論中給出的 bu Ivan Stoev 並且有效。

如果不使用反射,則無法關閉此功能,將“InternalContext.InitializerDisabled”屬性設置為 true 將使其跳過字典。

所以:

  • 使用提供 DbCachedModel 的 DbContext 構造函數
  • 使用 Database.SetInitializer(null);
  • 使用反射設置 InternalContext.InitializerDisabled = true

我用來測試這個示例的代碼,作為測試設置,我有 1 個帶有 30k 分區的主表,分區本身被查詢,因為 postgres(特別是 9.x)在大量分區時不能很好地擴展:

    public class PartContext : DbContext {
        private static readonly string _ConnectionString = new NpgsqlConnectionStringBuilder {
            Host = "localhost",
            Port = 5432,
            Database = "postgres",
            Username = "postgres",
            Password = "password"
        }.ConnectionString;

        public readonly string Table;
        public readonly string Partition;

        public PartContext(string pMainTable, string pPartition) : base(
            new NpgsqlConnection() { ConnectionString = _ConnectionString },
            PartDbModelBuilder.Get(_ConnectionString, pPartition),
            true
        ) {
            Table = pMainTable;
            Partition = pPartition;

            Database.SetInitializer<PartContext>(null);


            /**
             * Disable database initialization so that the DbCachedModels are not kept internally in Entity
             * This causes high memory usage when having a lot of tables 
             * In EF 6.4.2 there was no way to 'manage' that Dictionary externally
             */
            try {
                var InternalContext = typeof(PartContext).BaseType.GetProperty("InternalContext", BindingFlags.NonPublic | BindingFlags.Instance).GetValue(this, null);
                InternalContext.GetType().GetProperty("InitializerDisabled").SetValue(InternalContext, true);
            } catch(Exception) { }
        }

        public DbSet<MyPart> Parts { get; set; }

        protected override void OnModelCreating(DbModelBuilder modelBuilder) {
            modelBuilder.HasDefaultSchema("public");
            modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
        }
    }

這提供了 DbCachedModels:

我建議添加一些自定義緩存代碼等,這只是來自示例

    class PartDbModelBuilder {
        public static DbCompiledModel Get(string pConnectionString, string pTable) {
            DbModelBuilder builder = new DbModelBuilder();
            builder.Entity<MyPart>().ToTable(pTable, "public");
            using (var connection = new NpgsqlConnection() { ConnectionString = pConnectionString }) {
                var obj = builder.Build(connection).Compile();
                return obj;
            }
        }
    }

這是我用作測試的實體:

    public class MyPart {
        public int id { get; set; }
        public string name { get; set; }
        public string value { get; set; }
    }

我用來運行測試的類:

    class EFTest {
        public void Run(int tableCount) {
            int done = 0;
            Parallel.For(0, tableCount, new ParallelOptions { MaxDegreeOfParallelism = 5 }, (i) => {
                string id = i.ToString().PadLeft(5, '0');
                using (var context = new PartContext("mypart", "mypart_" + id)) {
                    var objResult = context.Parts.First();
                    Console.WriteLine(objResult.name);
                }
                done++;
                Console.WriteLine(done + " DONE");
            });
        }
    }

表定義:

    CREATE TABLE IF NOT EXISTS mypart (
        id SERIAL,
        name text,
        value text
    ) partition by list (name);

    CREATE TABLE IF NOT EXISTS part partition of mypart_00000 for values in ('mypart00000');
    CREATE TABLE IF NOT EXISTS part partition of mypart_00001 for values in ('mypart00001');
    CREATE TABLE IF NOT EXISTS part partition of mypart_00002 for values in ('mypart00002');
    ...

Postgres 9:

    CREATE TABLE IF NOT EXISTS mypart (
        id SERIAL,
        name text,
        value text
    );

    CREATE TABLE IF NOT EXISTS ".$name."( CHECK ( name =  'mypart00000')) INHERITS (mypart);
    CREATE TABLE IF NOT EXISTS ".$name."( CHECK ( name =  'mypart00001')) INHERITS (mypart);
    CREATE TABLE IF NOT EXISTS ".$name."( CHECK ( name =  'mypart00002')) INHERITS (mypart);
    ...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM