【发布时间】:2021-11-24 17:13:33
【问题描述】:
我有一个包含大量物联网数据的巨大数据表。该表是使用 ef-core codefirst 创建的:
public class IoTData : Auditierbar<IoTData, Guid>
{
public Guid IoTDeviceId { get; set; }
public Guid IoTDeviceInternalId { get; set; }
public TimeSpan Zeit { get; set; }
public string EventMessage { get; set; }
public DateTime Timestamp { get; set; }
public int Input1 { get; set; }
public int Input2 { get; set; }
public DateTime? SendData { get; set; }
public string IId { get; set; }
public string DeviceId { get; set; }
public bool TestData { get; set; }
public virtual IoTDevice IoTDevice { get; set; }
public virtual ICollection<IoTData_Rueckmeldung> IoTData_Feedbacks { get; set; }
public bool InputStatus1 { get; set; }
public bool InputStatus2 { get; set; }
}
public class IoTDataConfiguration : AuditierbarConfiguration<IoTData, Guid>
{
public override void Configure(EntityTypeBuilder<IoTData> builder)
{
base.Configure(builder);
builder.ToTable("MDE_IoTData");
builder.HasIndex(r => new { r.IoTDeviceId });
builder.HasIndex(r => new { r.UnternehmenId, r.Datum });
// builder.HasIndex(r => new { r.UnternehmenId, r.Datum, r.Zeit });
builder.HasIndex(r => new { r.UnternehmenId, r.Datum, r.IoTDeviceId });
//builder.HasIndex(r => new { r.UnternehmenId, r.Datum, r.Zeit, r.IoTDeviceId });
builder.HasIndex(r => new { r.UnternehmenId, r.IoTDeviceInternalId }).IsUnique();
builder.Property(r => r.Zeit).IsRequired().HasColumnType("Time(0)").HasDefaultValue(default);
builder.Property(r => r.EventMessage).IsRequired();
builder.Property(r => r.Datum).IsRequired().HasColumnType("Date").HasDefaultValueSql("GetUtcDate()");
}
您可以看到,已经创建了几个索引。你可以在这里看到我的查询:
declare @__companyId_0 as uniqueidentifier ='03d7fdd2-....-....-......'
declare @__starttime_Date_1 as date= '2021-9-30'
declare @__endtime_Date_2 as date ='2021-10-01'
--declare @__starttime_TimeOfDay_3 as time ='22:00:00'
--declare @__endtime_TimeOfDay_4 as time ='22:00:00'
SELECT [m].[IoTDeviceId], [m].[Datum], [m].[Zeit], [m].[Input1], [m].[Input2], [m].[Timestamp]
FROM [MDE_IoTData] AS [m]
WHERE ((([m].[UnternehmenId] = @__companyId_0) AND
([m].[Datum] >= @__starttime_Date_1)) AND
([m].[Datum] <= @__endtime_Date_2)) AND
[m].[IoTDeviceId] in(
'E87B3284-....-....-......',
'A363FE26-....-....-......',
'97924216-....-....-......',
'C8C50B8C-....-....-......',
'93F80183-....-....-......',
'F3572F35-....-....-......',
'6B19A81B-....-....-......',
'A17CBB46-....-....-......')
我想,这个索引很适合这个查询
builder.HasIndex(r => new { r.UnternehmenId, r.Datum, r.IoTDeviceId });
但我有时会遇到 sql-timeout-exceptions。会不会是我的索引不对? 每个 IoTDeviceId 最多可以有 1440 行。所以在那个查询(两天的数据)中,我期望最大值。 11520 行,需要 5 秒。如果日期范围更大,例如。 14天,查询时间超过一分钟。
有什么想法吗?
更新
查询计划: https://www.brentozar.com/pastetheplan/?id=B1CHVrOVF
表架构
CREATE TABLE [dbo].[MDE_IoTData](
[Id] [uniqueidentifier] NOT NULL,
[Erstellt] [datetime2](7) NOT NULL,
[Datum] [date] NOT NULL,
[UnternehmenId] [uniqueidentifier] NOT NULL,
[IoTDeviceId] [uniqueidentifier] NOT NULL,
[Zeit] [time](0) NOT NULL,
[EventMessage] [nvarchar](max) NOT NULL,
[Input1] [int] NOT NULL,
[Input2] [int] NOT NULL,
[IoTDeviceInternalId] [uniqueidentifier] NOT NULL,
[SendData] [datetime2](7) NULL,
[Timestamp] [datetime2](7) NOT NULL,
[InputStatus1] [bit] NOT NULL,
[InputStatus2] [bit] NOT NULL,
CONSTRAINT [PK_MDE_IoTData] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
GO
ALTER TABLE [dbo].[MDE_IoTData] ADD DEFAULT (getutcdate()) FOR [Erstellt]
GO
ALTER TABLE [dbo].[MDE_IoTData] ADD DEFAULT (getutcdate()) FOR [Datum]
GO
ALTER TABLE [dbo].[MDE_IoTData] ADD DEFAULT (N'') FOR [EventMessage]
GO
ALTER TABLE [dbo].[MDE_IoTData] ADD DEFAULT ((0)) FOR [Input1]
GO
ALTER TABLE [dbo].[MDE_IoTData] ADD DEFAULT ((0)) FOR [Input2]
GO
ALTER TABLE [dbo].[MDE_IoTData] ADD DEFAULT ('00000000-0000-0000-0000-000000000000') FOR [IoTDeviceInternalId]
GO
ALTER TABLE [dbo].[MDE_IoTData] ADD DEFAULT ('0001-01-01T00:00:00.0000000') FOR [Timestamp]
GO
ALTER TABLE [dbo].[MDE_IoTData] ADD DEFAULT (CONVERT([bit],(0))) FOR [InputStatus1]
GO
ALTER TABLE [dbo].[MDE_IoTData] ADD DEFAULT (CONVERT([bit],(0))) FOR [InputStatus2]
GO
ALTER TABLE [dbo].[MDE_IoTData] WITH CHECK ADD CONSTRAINT [FK_MDE_IoTData_Global_Unternehmen_UnternehmenId] FOREIGN KEY([UnternehmenId])
REFERENCES [dbo].[Global_Unternehmen] ([Id])
GO
ALTER TABLE [dbo].[MDE_IoTData] CHECK CONSTRAINT [FK_MDE_IoTData_Global_Unternehmen_UnternehmenId]
GO
ALTER TABLE [dbo].[MDE_IoTData] WITH CHECK ADD CONSTRAINT [FK_MDE_IoTData_MDE_IoTDevice_IoTDeviceId] FOREIGN KEY([IoTDeviceId])
REFERENCES [dbo].[MDE_IoTDevice] ([Id])
GO
ALTER TABLE [dbo].[MDE_IoTData] CHECK CONSTRAINT [FK_MDE_IoTData_MDE_IoTDevice_IoTDeviceId]
GO
【问题讨论】:
-
您在询问查询性能调优建议,因为您的 EF 代码几乎无关紧要,请使用PasteThePlan包含表架构定义和实际执行计划
-
一些快速观察 - 您的统计数据可以用于更新和很可能重建索引,您正在使用 guid 对行进行聚类(这是一个值得商榷的决定!)但这意味着您的聚集索引将随着时间的推移变得非常分散,当然所有您的非聚集索引都因这些 guid 值而膨胀,这对性能不利。几乎所有执行时间都花在此查询的键查找上 - 检索到的列相对较窄,因此建议的包含列值得添加以使其覆盖。
-
第一步是为聚集索引找到最佳选择——你可以只有一个,不要假设它应该是主键。搜索将找到关于选择最佳的讨论 - 这在很大程度上取决于您的使用情况。日期列通常是一个很好的聚类候选者。