1

我有一个非常大的表 ~55,000,000 条记录。最常用的列都添加了索引,但是表还是很慢。

关于如何提高表性能有什么建议吗?我曾考虑过对表进行分区,但不确定是否有必要。

--Table
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[EngineRecord](
    [Id] [uniqueidentifier] NOT NULL,
    [CreateDate] [datetime] NOT NULL,
    [ChangeDate] [datetime] NOT NULL,
    [CompanyId] [uniqueidentifier] NOT NULL,
    [DriverEmployeeId] [uniqueidentifier] NOT NULL,
    [EobrDeviceId] [uniqueidentifier] NOT NULL,
    [EobrTimestampUtc] [datetime] NOT NULL,
    [EobrOverallStatus] [int] NOT NULL,
    [Speedometer] [decimal](14, 4) NOT NULL,
    [Odometer] [decimal](14, 4) NOT NULL,
    [Tachometer] [decimal](14, 4) NOT NULL,
    [GpsTimestampUtc] [datetime] NULL,
    [GpsLatitude] [decimal](18, 8) NULL,
    [GPSLongitude] [decimal](18, 8) NULL,
    [RecordType] [int] NOT NULL,
    [FuelEconomyAverage] [decimal](8, 4) NOT NULL,
    [FuelEconomyInstant] [decimal](8, 4) NOT NULL,
    [FuelUseTotal] [decimal](14, 4) NOT NULL,
    [BrakePressure] [decimal](8, 4) NOT NULL,
    [CruiseControlSet] [bit] NOT NULL,
    [TransmissionAttained] [nvarchar](2) NULL,
    [TransmissionSelected] [nvarchar](2) NULL,
    [IsProcessed] [bit] NOT NULL,
    [LastChangedByUserId] [uniqueidentifier] NOT NULL,
 CONSTRAINT [PK_EngineRecord] PRIMARY KEY NONCLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = ON, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY],
 CONSTRAINT [NK_EngineRecord] UNIQUE CLUSTERED 
(
    [CompanyId] ASC,
    [EobrDeviceId] ASC,
    [EobrTimestampUtc] ASC
)WITH (PAD_INDEX  = ON, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[EngineRecord]  WITH NOCHECK ADD  CONSTRAINT [FK_EngineRecord_CompanyLevel] FOREIGN KEY([CompanyId])
REFERENCES [dbo].[CompanyLevel] ([Id])
GO

ALTER TABLE [dbo].[EngineRecord] CHECK CONSTRAINT [FK_EngineRecord_CompanyLevel]
GO

ALTER TABLE [dbo].[EngineRecord]  WITH NOCHECK ADD  CONSTRAINT [FK_EngineRecord_Employee] FOREIGN KEY([DriverEmployeeId])
REFERENCES [dbo].[Employee] ([Id])
ON DELETE CASCADE
GO

ALTER TABLE [dbo].[EngineRecord] CHECK CONSTRAINT [FK_EngineRecord_Employee]
GO

ALTER TABLE [dbo].[EngineRecord]  WITH NOCHECK ADD  CONSTRAINT [FK_EngineRecord_EobrDevice] FOREIGN KEY([EobrDeviceId])
REFERENCES [dbo].[EobrDevice] ([Id])
GO

ALTER TABLE [dbo].[EngineRecord] CHECK CONSTRAINT [FK_EngineRecord_EobrDevice]
GO



---------------------
--Indexes/Constraints
---------------------


ALTER TABLE [dbo].[EngineRecord] ADD  CONSTRAINT [PK_EngineRecord] PRIMARY KEY NONCLUSTERED 
(
    [Id] ASC
)WITH (PAD_INDEX  = ON, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
GO



CREATE NONCLUSTERED INDEX [NC_EngineRecord_Employee] ON [dbo].[EngineRecord] 
(
    [DriverEmployeeId] ASC
)WITH (PAD_INDEX  = ON, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
GO



CREATE NONCLUSTERED INDEX [NC_RecordType] ON [dbo].[EngineRecord] 
(
    [RecordType] ASC
)WITH (PAD_INDEX  = ON, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
GO



ALTER TABLE [dbo].[EngineRecord] ADD  CONSTRAINT [NK_EngineRecord] UNIQUE CLUSTERED 
(
    [CompanyId] ASC,
    [EobrDeviceId] ASC,
    [EobrTimestampUtc] ASC
)WITH (PAD_INDEX  = ON, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
GO



CREATE NONCLUSTERED INDEX [IX_EngineRecord_DBA] ON [dbo].[EngineRecord] 
(
    [CompanyId] ASC,
    [GpsLatitude] ASC,
    [GPSLongitude] ASC
)
INCLUDE ( [EobrDeviceId],
[EobrTimestampUtc]) WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 90) ON [PRIMARY]
GO



CREATE NONCLUSTERED INDEX [NC_IsProcessed] ON [dbo].[EngineRecord] 
(
    [IsProcessed] ASC
)WITH (PAD_INDEX  = ON, STATISTICS_NORECOMPUTE  = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON, FILLFACTOR = 80) ON [PRIMARY]
GO

编辑:

这是一个经常使用的需要一些时间才能运行的存储过程。

CREATE PROCEDURE [dbo].[EngineRecord__GetEobrListToProcessByRecordType]
    @RecordTypeEnum int
AS
DECLARE @ChangeHistory bit -- dummy variable for VS 2008 database project

SET NOCOUNT ON

SELECT EobrDevice.[Id] as EobrDeviceId, 
    EobrDevice.[UnitId], 
    CompanyGroupRoot.[Id] as CGRootId, 
    CompanyGroup.[Id] as CompanyGroupId, 
    EobrDevice.[CompanyId]
FROM dbo.EobrDevice
    INNER JOIN dbo.CompanyLevel ON EobrDevice.[CompanyId] = CompanyLevel.[Id]
    INNER JOIN dbo.CompanyGroup ON CompanyLevel.ParentGroupId = CompanyGroup.[Id]
    INNER JOIN dbo.CompanyGroupRoot ON CompanyGroup.CGRootId = CompanyGroupRoot.[Id]
WHERE EobrDevice.[Id] IN ( SELECT DISTINCT EngineRecord.EobrDeviceId FROM dbo.EngineRecord WHERE IsProcessed = 0 AND RecordType = @RecordTypeEnum ) 
    AND EobrDevice.UnitId IS NOT NULL

编辑2:

这是我们每天晚上运行以清除旧记录的东西。这总是需要很多时间。

DECLARE @dt6MonthsPrior datetime
    SET @dt6MonthsPrior = DATEADD(m, -6, getdate())

    SELECT * FROM EngineRecord
    WHERE EngineRecord.EobrTimeStampUtc < @dt6MonthsPrior
    ORDER BY EobrTimestampUtc ASC
4

5 回答 5

2

您的条件中的所有字段WHERE都不包含在索引中。索引这些字段会有所帮助。如果不更彻底地了解表格的使用方式,就无法确定其他指数的功效。

如果您真的希望此查询运行,您可以在里程表和转速表上创建一个聚集索引,但考虑到该表的其他用途,这可能不合理。

更新:

您的第二个存储过程似乎不应该非常慢,似乎唯一可以帮助的是日期索引。

这些天 5500 万条记录并没有那么大,我不是分区专家,但我认为你不会通过对表进行分区来看到任何改进,除非我希望表能够超过几亿条记录,但在生产环境中,分区还有其他好处。

您是否确定硬件不对您看到的性能不佳负责?SQL Server 中有许多设置/功能也会影响性能。

于 2013-08-29T16:10:24.643 回答
1

像这样的索引可能有助于这个特定的查询:

CREATE INDEX x ON dbo.EngineRecord(Odometer, Tachometer) WHERE FuelUseTotal IS NOT NULL;

如果您停止按时间戳排序,这将很有帮助。

于 2013-08-29T16:14:33.813 回答
1

你知道如何获得执行计划吗?您没有关于 tach 或 odo 或 FuelUse 的索引,因此您的示例查询将导致全表扫描。在 Sql Management Studio 中,右键单击查询窗口,选择“包括实际执行计划”,然后运行您的查询。您将看到一个输出,向您解释 SQL Server 实际运行查询必须执行的步骤。一旦您花时间了解执行计划,这将非常有启发性。

此外,您可能想要调查覆盖索引。如果您有一些经常使用的查询,这些可能会产生巨大的差异。当然,像任何索引一样,添加/删除时会有更多开销

于 2013-08-29T16:19:57.683 回答
0

像 Goat CO 建议的那样对字段进行索引WHERE是一个好的开始,我还建议将WHERE条件移至第一个INNER JOIN,这样为进一步处理而创建的临时表在第一个之后已经小得多INNER JOIN(我已经看到它的性能奇迹)

SELECT EobrDevice.[Id] as EobrDeviceId, 
    EobrDevice.[UnitId], 
    CompanyGroupRoot.[Id] as CGRootId, 
    CompanyGroup.[Id] as CompanyGroupId, 
    EobrDevice.[CompanyId]
FROM dbo.EobrDevice
INNER JOIN dbo.CompanyLevel 
    ON EobrDevice.UnitId IS NOT NULL
    AND EobrDevice.[CompanyId] = CompanyLevel.[Id]
    AND EobrDevice.[Id] IN (
        SELECT DISTINCT EngineRecord.EobrDeviceId 
        FROM dbo.EngineRecord 
        WHERE IsProcessed = 0 
          AND RecordType = @RecordTypeEnum
    )
INNER JOIN dbo.CompanyGroup ON CompanyLevel.ParentGroupId = CompanyGroup.[Id]
INNER JOIN dbo.CompanyGroupRoot ON CompanyGroup.CGRootId = CompanyGroupRoot.[Id]

我还移动了EobrDevice.UnitId IS NOT NULL要首先检查的条件,以便仅在满足该条件时才检查其他表并运行子查询。

于 2013-08-29T16:31:57.800 回答
0

分区索引应该会对您的性能产​​生影响。但它们必须在适当的单独驱动器内完成。您没有提供硬件信息(您在使用什么,NAS?SAS 驱动器?...)

此外,规范化并不总是关于您的流程目标的最佳选择,尤其是出于分析目的。在您的主表中非规范化的某些字段(CompanyLevel,CompanyGroup)会对您的选择产生更好的影响 - 好吧,每个主厨都有自己的厨房,所以让我们跳过这个讨论......

索引的构建不适合您清除数据的方式。如果你决定改变你的

[EobrTimestampUtc] ASC

改成

  [EobrTimestampUtc] DESC

将影响搜索索引EngineRecord.EobrTimeStampUtc < @dt6MonthsPrior

于 2013-09-14T05:42:59.757 回答