【问题标题】:Too much load time加载时间过长
【发布时间】:2014-02-26 12:09:28
【问题描述】:

我正在使用 MVC5 和 EF6 来开发应用程序。我将 SQL Server Express 用于数据库。 我在数据库中有两个表/实体。

  1. 车辆 - 包含有关车辆的信息
  2. VehicleLog - 包含从 GPS Tracker Fit 接收到的数据。

我的 VehicleLog 表目前有大约 20K 条记录,从表中获取特定车辆的数据大约需要 80 秒。 例如:我尝试获取最后一条记录以显示车辆的当前状态(即移动或停止),这需要超过 1 分半钟。

Vehicle log 表中的记录数会随着时间的推移而增加。

当我尝试使用服务器资源管理器打开表时,它会在 5-10 秒内显示所有数据。 任何人都可以帮助我在页面上快速加载详细信息。

感谢您阅读并关注问题。

我的代码:

 public ActionResult Dashboard()
    {
        ApplicationUser au = db.Users.Find(currentUser);

        var myVehicles = from vehicle in au.Vehicles.ToList()
                         where vehicle.License.ExpiryDate >= DateTime.Now && !vehicle.IsDeleted
                         select new CurrentVehicleStatus
                         {
                             VehicleName = vehicle.Name,
                             DriverName = vehicle.Driver != null ? vehicle.Driver.Name : "No driver",
                             DriverId=vehicle.Driver != null ? vehicle.Driver.DriverId : 0,
                             VehicleId = vehicle.VehicleId,
                             VehicleStatus = GeoUtils.GetStatusOf(vehicle.GsmDeviceLogs.Last())
                          };
    return PartialView("Dashboard", myVehicles);
    }


public static VehicleStatus GetStatusOf(GSMDeviceLog deviceLog)
    {
        VehicleStatus currentStatus = VehicleStatus.Stop;

        if (deviceLog != null)
        {
            //Considering DigitalInputLevel1 as Ignition. Not a correct way to do it as DigitalInputLevel1
            //is device dependent. Must change in future.
            if (deviceLog.DigitalInputLevel1)
                currentStatus = VehicleStatus.Idle;

            if (deviceLog.DigitalInputLevel1 && deviceLog.Speed > ZERO_SPEED)
                currentStatus = VehicleStatus.Moving;
            else if (!deviceLog.DigitalInputLevel1 && deviceLog.Speed >= ZERO_SPEED)
                currentStatus = VehicleStatus.Towed;
            if ((DateTime.Now - deviceLog.DateTimeOfLog).TotalMinutes > 2)
                currentStatus = VehicleStatus.Unreachable;
        }
        else
            currentStatus = VehicleStatus.Unreachable;

        return currentStatus;
    }

如果我评论最后一行(VehicleStats=....)页面的加载时间低于 1 秒。但是如果它评论了大约 2 分钟。

型号:

public class Vehicle
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int VehicleId
    { get; set; }

    [Display(Name = "Vehicle name")]
    [StringLength(100)]
    public String Name
    { get; set; }

    [Required]
    public String VehicleType
    { get; set; }

    [Required]
    [StringLength(20)]
    [Display(Name = "Registration no.")]
    public String RegNo
    { get; set; }

    [Required]
    [StringLength(100)]
    public String Manufacturer
    { get; set; }

    [StringLength(20)]
    [Display(Name = "Model or Year")]
    public String Year
    { get; set; }

    [StringLength(100)]
    [Display(Name = "Service provider")]
    public String ServiceProvider
    { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Insurance date")]
    [DisplayFormat(DataFormatString = "{0:dd/MM/yyyy}", ApplyFormatInEditMode = true)]
    public DateTime InsuranceDate
    { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "Last serviced on")]
    public DateTime LastServicedOn
    { get; set; }

    [Display(Name = "Last serviced at (km)")]
    public int LastServicedAt
    { get; set; }

    [Display(Name = "Next service at (km)")]
    public int NextServiceAt
    { get; set; }

    [DataType(DataType.Date)]
    [Display(Name = "PUC expiry date")]
    public DateTime PUCExpiryDate
    { get; set; }


    [Display(Name = "Vehicle Ownership document")]
    [DataType(DataType.ImageUrl)]
    public virtual List<OwnershipPaper> OwnershipPapers
    { get; set; }

    //[Display(Name = "Vehicle status")]
    //public VehicleStatusType VehicleStatus
    //{ get; set; }

    [Display(Name = "Target Utilization (km) per day")]
    public int TargetUtilizationPerDay
    { get; set; }

    public virtual Driver Driver
    { get; set; }
    [Display(Name = "Vehicle Group")]

    [Required]
    public virtual VehicleGroup VehicleGroup
    { get; set; }

    public string IMEI 
    { get; set; }

    [Display(Name="Fuel tank capacity")]
    [Required]
    public double FuelTankCapacityLitres
    { get; set; }

    public virtual License License { get; set; }

    public virtual ICollection<GSMDeviceLog> GsmDeviceLogs { get; set; }

    public virtual Policy Policy { get; set; }

    public virtual ApplicationUser User { get; set; }

    public bool IsDeleted { get; set; }
}



public class GSMDeviceLog
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int GSMDeviceLogId { get; set; }
    public string IMEI { get; set; }
    public string Message { get; set; }
    public string ProfileName { get; set; }
    public bool GPSStatus { get; set; }
    public int SignalStrength { get; set; }
    public DateTime DateTimeOfLog { get; set; }
    //public string TimeOfLog { get; set; }
    public double Latitude { get; set; }
    public double Longitude { get; set; }
    public float Altitude { get; set; }
    public float Speed { get; set; }
    public float Direction { get; set; }
    public int NoOfSatelite { get; set; }
    public float GPSPositionAccuracyIndication { get; set; }
    public float MilageReading { get; set; }
    public string Cell { get; set; }
    public float AnalogInputVoltage1 { get; set; }
    public float AnalogInputVoltage2 { get; set; }
    public float AnalogInputVoltage3 { get; set; }
    public float AnalogInputVoltage4 { get; set; }
    public bool DigitalInputLevel1 { get; set; }
    public bool DigitalInputLevel2 { get; set; }
    public bool DigitalInputLevel3 { get; set; }
    public bool DigitalInputLevel4 { get; set; }
    public bool DigitalOutputLevel1 { get; set; }
    public bool DigitalOutputLevel2 { get; set; }
    public bool DigitalOutputLevel3 { get; set; }
    public bool DigitalOutputLevel4 { get; set; }

    [Display(Name="Address")]
    public string Location { get; set; }

    public int InfoNumber { get; set; }

    //Reperesent harsh accelration and deaccelration
    public bool HarshDetecation { get; set; }

    //RFID Tag Number
    [StringLength(15)]
    public string RFID { get; set; }

    //public virtual Policy Policy { get; set; }

    public virtual ICollection<Violation> Violations { get; set; }

    public virtual Vehicle Vehicle { get; set; }


}

【问题讨论】:

  • 请提供有关您的表格的更多信息。你的表的结构、主键、数据类型...
  • 请出示您的代码。
  • 请检查编辑。
  • 什么是GeoUtils.GetStatusOf
  • 它是一个检查日志速度以判断车辆是移动还是停止的功能。

标签: entity-framework asp.net-mvc-5


【解决方案1】:

问题在于您对 ToList() 的调用。 这会评估查询并从数据库中检索每一行。

然后您在循环中调用导航属性。这将导致为数据库中的每个项目执行一个新查询。这就是运行速度慢的原因。

如果您只是将 'ToList()' 调用移到 'select' 语句之后,所有问题都将得到解决。

myVehicles = from vehicle in au.Vehicles
    where vehicle.License.ExpiryDate >= DateTime.Now && !vehicle.IsDeleted
    select new
    {
        VehicleName = vehicle.Name,
        DriverName = vehicle.Driver.Name ?? "No driver",
        DriverId= vehicle.Driver == null ? 0 : vehicle.Driver.DriverId,
        VehicleId = vehicle.VehicleId,
        GsmDeviceLogs = vehicle.GsmDeviceLogs.LastOrDefault()
    }.ToList()
    .Select({vehicle => new CurrentVehicleStatus
        VehicleName = VehicleName,
        DriverName = DriverName,
        DriverId= DriverId,
        VehicleId = VehicleId,
        VehicleStatus = GeoUtils.GetStatusOf(GsmDeviceLogs)
    });

编辑:将三元运算符更改为空合并,以使它们对 EntityFramework 友好(驱动程序是否为空无关紧要,因为这是在后台转换为 SQL 的表达式)。

编辑:看到 GeoUtils 的代码,更新了答案。它肯定不会在表达式树中被解析。此外,更改了对 LastOrDefault() 的调用。

【讨论】:

  • 感谢您的尝试。但是最后一行 VehicleStatus=.... 仍然需要太多时间。
  • 我无法解释这一点。您应该注意到移动 ToList() 语句的速度显着提高。您为每辆车保存了对 SQL 服务器的三个调用。
  • DriverId 不能这样分配,它说 Operator '??'不能应用于“int”和“int”类型的操作数。
  • 嗯。这不是主要问题。问题是加载时间....还有其他选项可以解决这个问题吗?
  • 这将修复加载时间。在 to list 调用之前的所有内容都被翻译成 sql。这意味着您只需调用服务器一次,而不是每个项目一次。由于往返服务器非常昂贵,因此在性能方面是一个巨大的胜利。
【解决方案2】:

您的查询实际上是内存中的 LINQ to Objects,而不是数据库查询 (LINQ to Entities),因为源集合是 au.Vehicles,它是内存中的集合。但是,由于延迟加载,涉及到几个数据库查询,它们在后台加载的数据比您需要的多得多:

  • au.Vehicles 是延迟加载的,即用户au所有 车辆 -> 1 个数据库查询
  • Vehicle.License 被延迟加载 -> 每辆车 1 个数据库查询(满足 where 过滤器)
  • Vehicle.Driver 被延迟加载 -> 每辆车 1 个数据库查询(满足 where 过滤器)
  • Vehicle.GsmDeviceLogs 被延迟加载,即所有车辆日志 -> 每辆车 1 个数据库查询(满足 where 过滤器)

所有这些延迟加载查询很可能是性能不佳的原因。

为了确保整个事情是一个只加载所需数据的单一数据库查询,您必须尽可能多地从DbSet&lt;T&gt;/IQueryable&lt;T&gt; 编写查询。在您的情况下,尤其要避免将Find 用于ApplicationUser。它可能看起来像这样:

var myVehicles = db.Users
    .Where(u => u.UserName == currentUser)
    .SelectMany(u => u.Vehicles)
    .Where(v => v.License.ExpiryDate >= DateTime.Now && !v.IsDeleted)
    .Select(v => new
    {
        VehicleName = v.Name,
        DriverName = v.Driver != null ? v.Driver.Name : "No driver",
        DriverId = v.Driver != null ? v.Driver.DriverId : 0,
        VehicleId = v.VehicleId,
        LastGsmDeviceLog = v.GsmDeviceLogs
            .OrderByDescending(gdl => gdl.CreateDateTime)
            .FirstOrDefault()
    })
    .AsEnumerable()
    .Select(x => new CurrentVehicleStatus
    {
        VehicleName = x.VehicleName,
        DriverName = x.DriverName,
        DriverId = x.DriverId,
        VehicleId = x.VehicleId,
        VehicleStatus = GeoUtils.GetStatusOf(x.LastGsmDeviceLog)
    });

正如 Max 提到的 GeoUtils.GetStatusOf 不能被翻译成 SQL,所以你必须以 AsEnumerable() 结束数据库查询(它比 ToList() 具有更少的内存开销)并执行最终的 Select 并调用GetStatusOf 在内存中。

【讨论】:

  • 感谢您的帮助。你的解释和回答都非常好。
  • +1 for AsEnumerable() 和比我的更好的书面答案。
【解决方案3】:

它会做很多抓取。尝试 Max 方法,但将无法转换为 SQL 的 LastOrDefault() 更改为 OrderBy 加上 FirstOrDefault。像这样的

myVehicles = from vehicle in au.Vehicles
where vehicle.License.ExpiryDate >= DateTime.Now && !vehicle.IsDeleted
select new
{
    VehicleName = vehicle.Name,
    DriverName = vehicle.Driver.Name ?? "No driver",
    DriverId= vehicle.Driver == null ? 0 : vehicle.Driver.DriverId,
    VehicleId = vehicle.VehicleId,
    GsmDeviceLogs = vehicle.GsmDeviceLogs.OrderByDescending(l => l.Id).FirstOrDefault()
}.ToList()
.Select({vehicle => new CurrentVehicleStatus
    VehicleName = VehicleName,
    DriverName = DriverName,
    DriverId= DriverId,
    VehicleId = VehicleId,
    VehicleStatus = GeoUtils.GetStatusOf(GsmDeviceLogs)
});

结果应该是“外部应用”SQL 语法。

【讨论】:

    猜你喜欢
    • 2012-08-23
    • 2016-06-20
    • 2016-02-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-17
    • 2016-03-01
    • 2019-12-24
    相关资源
    最近更新 更多