Đây là sự cố: Tôi đang thực hiện một truy vấn chọn. Mỗi cột trong mệnh đề WHERE
và ORDER BY
trong một chỉ mục không được nhóm IX_MachineryId_DateRecorded
, là một phần của khóa hoặc dưới dạng INCLUDE
cột. Tôi đang chọn tất cả các cột, do đó sẽ dẫn đến việc tra cứu dấu trang, nhưng tôi chỉ lấy TOP (1)
, vì vậy chắc chắn máy chủ có thể cho biết việc tra cứu chỉ cần được thực hiện một lần, vào cuối.
Quan trọng nhất, khi tôi buộc truy vấn sử dụng chỉ mục IX_MachineryId_DateRecorded
, nó sẽ chạy trong chưa đầy một giây. Nếu tôi để máy chủ quyết định sử dụng chỉ mục nào, nó sẽ chọn IX_MachineryId
và mất tới một phút. Điều đó thực sự gợi ý cho tôi rằng tôi đã thực hiện đúng chỉ mục và máy chủ đang đưa ra một quyết định tồi tệ. Tại sao?
CREATE TABLE [dbo].[MachineryReading] (
[Id] INT IDENTITY (1, 1) NOT NULL,
[Location] [sys].[geometry] NULL,
[Latitude] FLOAT (53) NOT NULL,
[Longitude] FLOAT (53) NOT NULL,
[Altitude] FLOAT (53) NULL,
[Odometer] INT NULL,
[Speed] FLOAT (53) NULL,
[BatteryLevel] INT NULL,
[PinFlags] BIGINT NOT NULL,
[DateRecorded] DATETIME NOT NULL,
[DateReceived] DATETIME NOT NULL,
[Satellites] INT NOT NULL,
[HDOP] FLOAT (53) NOT NULL,
[MachineryId] INT NOT NULL,
[TrackerId] INT NOT NULL,
[ReportType] NVARCHAR (1) NULL,
[FixStatus] INT DEFAULT ((0)) NOT NULL,
[AlarmStatus] INT DEFAULT ((0)) NOT NULL,
[OperationalSeconds] INT DEFAULT ((0)) NOT NULL,
CONSTRAINT [PK_dbo.MachineryReading] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_dbo.MachineryReading_dbo.Machinery_MachineryId] FOREIGN KEY ([MachineryId]) REFERENCES [dbo].[Machinery] ([Id]) ON DELETE CASCADE,
CONSTRAINT [FK_dbo.MachineryReading_dbo.Tracker_TrackerId] FOREIGN KEY ([TrackerId]) REFERENCES [dbo].[Tracker] ([Id]) ON DELETE CASCADE
);
GO
CREATE NONCLUSTERED INDEX [IX_MachineryId]
ON [dbo].[MachineryReading]([MachineryId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_TrackerId]
ON [dbo].[MachineryReading]([TrackerId] ASC);
GO
CREATE NONCLUSTERED INDEX [IX_MachineryId_DateRecorded]
ON [dbo].[MachineryReading]([MachineryId] ASC, [DateRecorded] ASC)
INCLUDE([OperationalSeconds], [FixStatus]);
Bảng được phân chia thành các phạm vi tháng (mặc dù tôi vẫn không thực sự hiểu những gì đang diễn ra ở đó).
ALTER PARTITION SCHEME PartitionSchemeMonthRange NEXT USED [Primary]
ALTER PARTITION FUNCTION [PartitionFunctionMonthRange]() SPLIT RANGE(N'2016-01-01T00:00:00.000')
ALTER PARTITION SCHEME PartitionSchemeMonthRange NEXT USED [Primary]
ALTER PARTITION FUNCTION [PartitionFunctionMonthRange]() SPLIT RANGE(N'2016-02-01T00:00:00.000')
...
CREATE UNIQUE CLUSTERED INDEX [PK_dbo.MachineryReadingPs] ON MachineryReading(DateRecorded, Id) ON PartitionSchemeMonthRange(DateRecorded)
Truy vấn mà tôi thường chạy:
SELECT TOP (1) [Id], [Location], [Latitude], [Longitude], [Altitude], [Odometer], [ReportType], [FixStatus], [AlarmStatus], [Speed], [BatteryLevel], [PinFlags], [DateRecorded], [DateReceived], [Satellites], [HDOP], [OperationalSeconds], [MachineryId], [TrackerId]
FROM [dbo].[MachineryReading]
--WITH(INDEX(IX_MachineryId_DateRecorded)) --This makes all the difference
WHERE ([MachineryId] = @p__linq__0) AND ([DateRecorded] >= @p__linq__1) AND ([DateRecorded] < @p__linq__2) AND ([OperationalSeconds] > 0)
ORDER BY [DateRecorded] ASC
Gói truy vấn: https://www.brentozar.com/pastetheplan/?id=r1c-RpxNx
Gói truy vấn với chỉ mục bắt buộc: https://www.brentozar.com/pastetheplan/?id=SywwTagVe
Các kế hoạch bao gồm là các kế hoạch thực hiện thực tế, nhưng trên cơ sở dữ liệu dàn (khoảng 1/100 kích thước của cuộc sống). Tôi do dự khi loay hoay với cơ sở dữ liệu trực tiếp vì tôi chỉ mới bắt đầu ở công ty này khoảng một tháng trước.
Tôi có cảm giác đó là do phân vùng và truy vấn của tôi thường kéo dài mọi phân vùng duy nhất (ví dụ: khi tôi muốn nhận bản ghi đầu tiên hoặc cuối cùng OperationalSeconds
được ghi cho một máy). Tuy nhiên, tất cả các truy vấn tôi đã viết bằng tay đều chạy nhanh hơn 10 - 100 lần so với những gì EntityFramework đã tạo, vì vậy tôi sẽ thực hiện một quy trình được lưu trữ.