重庆熊猫 Loading

Entity Framework教程-性能优化(Performance)

更新记录
转载请注明出处:
2022年10月28日 发布。
2022年10月22日 从笔记迁移到博客。

存储大量数值优先考虑Array,而不是List

原因是,list以对象格式存储数据,当我们首先尝试存储值类型时,它将其转换为引用类型,然后再存储

List<int> list = new List<int>();
int[] arr = new int[1000000];

//测试List
Stopwatch stopwatchForList = new Stopwatch();
stopwatchForList.Start();
for (int i = 0; i < 1000000; i++)
{
    list.Add(i);
    arr[i] = i;
}
stopwatchForList.Stop();

//测试Array
Stopwatch stopwatchForArray = new Stopwatch();
stopwatchForArray.Start();
for (int i = 0; i < 1000000; i++)
{
    arr[i] = i;
}
stopwatchForArray.Stop();

Console.WriteLine($"List花费时间{stopwatchForList.ElapsedMilliseconds}");
Console.WriteLine($"Array花费时间{stopwatchForArray.ElapsedMilliseconds}");

如果可以,优先考虑使用for,而不是foreach
//测试使用的List
List<int> list1 = new List<int>();
List<int> list2 = new List<int>();

//写入测试使用的数据
for (int i = 0; i < 100000; i++)
{
    list1.Add(i);
    list2.Add(i);
}

//读取测试-使用for
Stopwatch stopwatch1 = new Stopwatch();
stopwatch1.Start();
for (int i = 0; i < 100000; i++)
{
    Console.WriteLine(list1[i]);
}
stopwatch1.Stop();

//读取测试-使用foreach
Stopwatch stopwatch2 = new Stopwatch();
stopwatch2.Start();
foreach (int item in list2)
{
    Console.WriteLine(item);
}
stopwatch2.Stop();

//读取测试结果
Console.WriteLine($"读取测试结果1{stopwatch1.ElapsedMilliseconds}");
Console.WriteLine($"读取测试结果2{stopwatch2.ElapsedMilliseconds}");

存储大量组合的数据到内存时

如果是写入多读取少,考虑使用struct类型

如果是写入少读取多,考虑使用class类型

using System;
using System.Collections.Generic;
using System.Diagnostics;

namespace ConsoleApp1
{
    /// <summary>
    /// 测试使用的结构类型
    /// </summary>
    public struct PandaStruct
    {
        public string Code { get; set; }
        public string Name { get; set; }
    }

    /// <summary>
    /// 测试使用的类类型
    /// </summary>
    public class PandaClass
    {
        public string Code { get; set; }
        public string Name { get; set; }
    }
    class Program
    {
        static void Main(string[] args)
        {

            //测试使用的类型
            PandaStruct[] testStrcut = new PandaStruct[100000];
            PandaClass[] testClass = new PandaClass[100000];

            //写入数据测试-结构类型
            Stopwatch stopwatch1 = new Stopwatch();
            stopwatch1.Start();
            for (int i = 0; i < 100000; i++)
            { 
                testStrcut[i].Code = i.ToString();
                testStrcut[i].Name = "Panda" + i.ToString();
            }
            stopwatch1.Stop();

            //写入数据测试-类类型
            Stopwatch stopwatch2 = new Stopwatch();
            stopwatch2.Start();
            for (int i = 0; i < 100000; i++)
            {
                testClass[i] = new PandaClass();
                testClass[i].Code = i.ToString();
                testClass[i].Name = "Panda" + i.ToString();
            }
            stopwatch2.Stop();

            //写入测试结果
            Console.WriteLine("写入测试结果");
            Console.WriteLine($"struct写入花费时间:{stopwatch1.ElapsedMilliseconds}");
            Console.WriteLine($"class写入花费时间:{stopwatch2.ElapsedMilliseconds}");

            //读取测试-struct
            stopwatch1.Reset();
            stopwatch1.Start();
            for (int i = 0; i < 100000; i++)
            {
                Console.WriteLine(testStrcut[i].Code);
                Console.WriteLine(testStrcut[i].Name);
            }
            stopwatch1.Stop();

            //读取测试-class
            stopwatch2.Reset();
            stopwatch2.Start();
            for (int i = 0; i < 100000; i++)
            {
                Console.WriteLine(testClass[i].Code);
                Console.WriteLine(testClass[i].Name);
            }
            stopwatch2.Stop();

            //读取测试结果
            Console.WriteLine("读取测试结果");
            Console.WriteLine($"struct读取花费时间:{stopwatch1.ElapsedMilliseconds}");
            Console.WriteLine($"class读取花费时间:{stopwatch2.ElapsedMilliseconds}");

            //wait
            Console.ReadKey();
        }
    }
}

使用Stringbuilder进行字符串连接操作

LINQ查询操作不要启用变化跟踪

使用LINQ中的.AsNoTracking()方法,禁用变化跟踪

var result = dbContext.Students
                    .AsNoTracking()
                    .ToListAsync();

或者在上下文对象中直接关闭变化追踪
注意:一般不建议直接关闭,更新和添加数据操作需要变化追踪的支持

dbContext.ChangeTracker.QueryTrackingBehavior = QueryTrackingBehavior.NoTracking;

限制查询的条数

使用Take()方法限制查询的条数

var result = dbContext.Students
                    .AsNoTracking()
                    .Take(10)
                    .ToListAsync();

避免查询不需要的列(Unnecessary volume returned)

var result = dbContext.Students
    .OrderBy(s => s.StudentId)
    .Select(s => new { s.Name })
    .SingleOrDefault(s => s.Name == "Panda666");

避免查询的列数据类型不匹配(Mismatched data types)

列数据类型不匹配,会导致底层的SQL执行效率底下

var result = dbContext.Students
.Where(s => s.StudentId.Equals("9876543210"))
//字符串类型 与 数值类型 不匹配
.Where(s => s.StudentId.Equals(666))
//匹配
    .SingleOrDefault(s => s.Name == "Panda666");

单条数据查询使用.SingleOrDefault()

var result = dbContext.Students
    .OrderBy(s=>s.StudentId)
    .SingleOrDefault();

插入/更新大量数据时,先关闭EF的自动变化跟踪

//关闭自动变化跟踪
dbContext.ChangeTracker.AutoDetectChangesEnabled = false;

//大量的插入操作
for (int i = 0; i < 1000; i++)
{
    dbContext.Students
    .Add(new Student() { Name = "Panda" });
}

//开启自动变化跟踪
dbContext.ChangeTracker.AutoDetectChangesEnabled = true;

优先使用异步操作(Asynchronous operations)

  • The request reaching the ASP.NET pipeline will be allocated to a thread, which takes care of the execution
  • The thread will be occupied until the request is complete
  • If the action consumes any long-running process in a synchronous way, then the thread will be blocked until the process execution is complete. If all the threads in the thread pool is occupied, then it cannot serve additional requests
  • If the long-running process is implemented in an asynchronous way, then once the action is triggered, the thread will be released back to the thread pool, the released thread would be allocated to new request
  • Once the long-running process is complete, a new thread will be allocated to complete the request execution

避免N+1次查询

EF默认是延迟加载的,实体的附属实体,如果不使用提前加载,会导致再次查询操作
使用.Include()方法可以实现预加载

var result = dbContext.Students
        .Include(s=>s.Courses)
        .SingleOrDefault(s => s.Name == "Panda666");
posted @ 2022-10-28 09:19  重庆熊猫  阅读(94)  评论(0编辑  收藏  举报