Tôi có một vấn đề lớn với các đột biến CPU 100% vì một kế hoạch thực thi tồi được sử dụng bởi một truy vấn cụ thể. Tôi dành hàng tuần để giải quyết bằng chính tôi.
Cơ sở dữ liệu của tôi
DB mẫu của tôi chứa 3 bảng được đơn giản hóa.
[Bộ dữ liệu]
CREATE TABLE [model].[DataLogger](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[ProjectID] [bigint] NULL,
CONSTRAINT [PK_DataLogger] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
[Biến tần]
CREATE TABLE [model].[Inverter](
[ID] [bigint] IDENTITY(1,1) NOT NULL,
[SerialNumber] [nvarchar](50) NOT NULL,
CONSTRAINT [PK_Inverter] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY],
CONSTRAINT [UK_Inverter] UNIQUE NONCLUSTERED
(
[DataLoggerID] ASC,
[SerialNumber] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]
ALTER TABLE [model].[Inverter] WITH CHECK
ADD CONSTRAINT [FK_Inverter_DataLogger]
FOREIGN KEY([DataLoggerID])
REFERENCES [model].[DataLogger] ([ID])
[Biến tần]
CREATE TABLE [data].[InverterData](
[InverterID] [bigint] NOT NULL,
[Timestamp] [datetime] NOT NULL,
[DayYield] [decimal](18, 2) NULL,
CONSTRAINT [PK_InverterData] PRIMARY KEY CLUSTERED
(
[InverterID] ASC,
[Timestamp] ASC
)WITH (STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF)
)
Số liệu thống kê và bảo trì
Các [InverterData]
bảng chứa nhiều triệu hàng (khác ở chỗ nhiều trường PaaS) phân chia trong chiếc thuyền hàng tháng.
Tất cả các bộ chỉ mục được phân mảnh và tất cả các chỉ số xây dựng lại / tổ chức lại khi cần thiết trên một lượt hàng ngày / hàng tuần.
Sự truy vấn của tôi
Truy vấn là Entity Framework được tạo và cũng đơn giản. Nhưng tôi chạy 1.000 lần mỗi phút và hiệu suất là điều cần thiết.
SELECT
[Extent1].[InverterID] AS [InverterID],
[Extent1].[DayYield] AS [DayYield]
FROM [data].[InverterDayData] AS [Extent1]
INNER JOIN [model].[Inverter] AS [Extent2] ON [Extent1].[InverterID] = [Extent2].[ID]
INNER JOIN [model].[DataLogger] AS [Extent3] ON [Extent2].[DataLoggerID] = [Extent3].[ID]
WHERE ([Extent3].[ProjectID] = @p__linq__0)
AND ([Extent1].[Date] = @p__linq__1) OPTION (MAXDOP 1)
Các MAXDOP 1
gợi ý là cho một vấn đề khác với một kế hoạch song song chậm.
Kế hoạch "tốt"
Trong 90% thời gian, kế hoạch được sử dụng nhanh như chớp và trông như thế này:
Vấn đề
Trong ngày, kế hoạch tốt ngẫu nhiên thay đổi thành kế hoạch xấu và chậm.
Kế hoạch "xấu" được sử dụng trong 10-60 phút và sau đó đổi lại thành kế hoạch "tốt". Kế hoạch "xấu" tăng vọt CPU lên đến 100% vĩnh viễn.
Cái này nó thì trông như thế nào:
Những gì tôi cố gắng cho đến nay
Suy nghĩ đầu tiên của tôi Hash Match
là cậu bé hư. Vì vậy, tôi đã sửa đổi truy vấn với một gợi ý mới.
...Extent1].[Date] = @p__linq__1) OPTION (MAXDOP 1, LOOP JOIN)
Các LOOP JOIN
lực lượng nên sử dụng Nested Loop
ngay lập tức Hash Match
.
Kết quả là kế hoạch 90% trông giống như trước đây. Nhưng kế hoạch cũng thay đổi ngẫu nhiên thành xấu.
Kế hoạch "xấu" bây giờ trông như thế này (thứ tự vòng lặp bảng đã thay đổi):
CPU cũng liếc tới 100% trong kế hoạch "xấu mới".
Giải pháp?
Tôi nghĩ rằng để buộc kế hoạch "tốt". Nhưng tôi không biết nếu đây là một ý tưởng tốt.
Bên trong kế hoạch là một chỉ số được đề xuất bao gồm tất cả các cột. Nhưng điều này sẽ tăng gấp đôi bảng hoàn chỉnh và làm chậm tốc độ cao thường xuyên.
Làm ơn giúp tôi!
Cập nhật 1 - liên quan đến bình luận @James
Dưới đây là cả hai gói (một số trường bổ sung được hiển thị trong kế hoạch vì nó từ bảng thực):
Kế hoạch xấu 2 (Vòng lặp lồng nhau)
Cập nhật 2 - liên quan đến câu trả lời @David Fowler
Kế hoạch xấu là đá vào giá trị tham số ngẫu nhiên. Vì vậy, bình thường tôi @p__linq__1 ='2016-11-26 00:00:00.0000000' @p__linq__0 =20825
ngày lỗ và hơn là kế hoạch xấu đến cùng một giá trị.
Tôi biết vấn đề đánh hơi thông số từ các thủ tục được lưu trữ và cách tránh chúng trong SP. Bạn có gợi ý cho tôi cách tránh vấn đề này cho truy vấn của tôi không?
Tạo chỉ mục được đề xuất sẽ bao gồm tất cả các cột. Điều này sẽ tăng gấp đôi bảng hoàn chỉnh và làm chậm quá trình chèn, vốn thường xuyên cao. Điều đó không "cảm thấy" đúng khi xây dựng một chỉ mục chỉ đơn giản là sao chép bảng. Ngoài ra tôi có nghĩa là tăng gấp đôi kích thước dữ liệu của bảng lớn này.
Cập nhật 3 - liên quan đến bình luận @David Fowler
Nó cũng không hoạt động và tôi nghĩ nó không thể. Để hiểu rõ hơn tôi sẽ giải thích cho bạn cách truy vấn được gọi.
Giả sử tôi có 3 thực thể trong [DataLogger]
bảng. Cả ngày tôi gọi 3 truy vấn giống nhau trong một vòng lặp đi lặp lại:
Truy vấn cơ sở:
...WHERE ([Extent3].[ProjectID] = @p__linq__0) AND ([Extent1].[Date] = @p__linq__1)
Tham số:
@p__linq__0 = 1; @p__linq__1 = '2018-01-05 00:00:00.0000000'
@p__linq__0 = 2; @p__linq__1 = '2018-01-05 00:00:00.0000000'
@p__linq__0 = 3; @p__linq__1 = '2018-01-05 00:00:00.0000000'
Tham số @p__linq__1
luôn luôn là cùng một ngày. Nhưng nó chọn kế hoạch xấu một cách ngẫu nhiên trên một truy vấn chạy lần lượt với một kế hoạch tốt trước đó. Với cùng một tham số!
Cập nhật 4 - liên quan đến bình luận @Nic
Việc bảo trì chạy mỗi đêm và trông như thế này.
Mục lục
Nếu một Index bị phân mảnh nhiều hơn 5% thì nó được tổ chức lại ...
ALTER INDEX [{index}] ON [{table}] REORGANIZE
Nếu một Index bị phân mảnh nhiều hơn 30% thì nó sẽ được xây dựng lại ...
ALTER INDEX [{index}] ON [{table}] REBUILD WITH (ONLINE=ON, MAXDOP=1)
Nếu Index được phân vùng, nó sẽ được chứng minh về sự phân mảnh và thay đổi trên mỗi phân vùng ...
ALTER INDEX [{index}] ON [{table}] REBUILD PARTITION = {partitionNr} WITH (ONLINE=ON, MAXDOP=1)
Số liệu thống kê
Tất cả các số liệu thống kê sẽ được cập nhật nếu modification_counter
cao hơn 0 ...
UPDATE STATISTICS [{schema}].[{object}] ([{stats}]) WITH FULLSCAN
hoặc trên phân vùng ..
UPDATE STATISTICS [{schema}].[{object}] ([{stats}]) WITH RESAMPLE ON PARTITIONS({partitionNr})
Việc bảo trì bao gồm tất cả các số liệu thống kê, cũng là số liệu tự động tạo.