为大数据表找到合适的索引
Find the right Index for big datatable
我有大量数据table,其中包含大量物联网数据。 table 是使用 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-超时异常。难道是我的索引不对?
每个 IoTDeviceId 最多可以有 1440 行。所以在那个查询中(两天的数据)我预计最多。 11520 行,需要 5 秒。如果日期范围更大,例如。 14天,查询耗时一分多钟
有什么想法吗?
更新
查询计划:
https://www.brentozar.com/pastetheplan/?id=B1CHVrOVF
Table-架构
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
此查询的最佳索引最有可能
(UnternehmenId, Datum) INCLUDE (IoTDeviceId, IoTDeviceId, Zeit, Input1, Input2, [Timestamp])
这称为 覆盖 索引,因为它覆盖了查询中的所有列。最好将这些列保留在 INCLUDE
中,但是您可能需要使用手动 SQL 语句来执行此操作,而不是使用 HasIndex()
我有大量数据table,其中包含大量物联网数据。 table 是使用 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-超时异常。难道是我的索引不对? 每个 IoTDeviceId 最多可以有 1440 行。所以在那个查询中(两天的数据)我预计最多。 11520 行,需要 5 秒。如果日期范围更大,例如。 14天,查询耗时一分多钟
有什么想法吗?
更新
查询计划: https://www.brentozar.com/pastetheplan/?id=B1CHVrOVF
Table-架构
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
此查询的最佳索引最有可能
(UnternehmenId, Datum) INCLUDE (IoTDeviceId, IoTDeviceId, Zeit, Input1, Input2, [Timestamp])
这称为 覆盖 索引,因为它覆盖了查询中的所有列。最好将这些列保留在 INCLUDE
中,但是您可能需要使用手动 SQL 语句来执行此操作,而不是使用 HasIndex()