TOP (1) THEO NHÓM của bảng rất lớn (100.000.000+)


8

Thiết lập

Tôi có một bảng khổng lồ ~ 115.382.254 hàng. Bảng tương đối đơn giản và ghi lại các hoạt động quy trình ứng dụng.

CREATE TABLE [data].[OperationData](
    [SourceDeciveID] [bigint] NOT NULL,
    [FileSource] [nvarchar](256) NOT NULL,
    [Size] [bigint] NULL,
    [Begin] [datetime2](7) NULL,
    [End] [datetime2](7) NOT NULL,
    [Date]  AS (isnull(CONVERT([date],[End]),CONVERT([date],'19000101',(112)))) PERSISTED NOT NULL,
    [DataSetCount] [bigint] NULL,
    [Result] [int] NULL,
    [Error] [nvarchar](max) NULL,
    [Status] [int] NULL,
 CONSTRAINT [PK_OperationData] PRIMARY KEY CLUSTERED 
(
    [SourceDeviceID] ASC,
    [FileSource] ASC,
    [End] ASC
))

CREATE TABLE [model].[SourceDevice](
    [ID] [bigint] IDENTITY(1,1) NOT NULL,
    [Name] [nvarchar](50) NULL,
 CONSTRAINT [PK_DataLogger] PRIMARY KEY CLUSTERED 
(
    [ID] ASC
))

ALTER TABLE [data].[OperationData]  WITH CHECK ADD  CONSTRAINT [FK_OperationData_SourceDevice] FOREIGN KEY([SourceDeviceID])
REFERENCES [model].[SourceDevice] ([ID])

Bảng được nhóm ở khoảng 500 cụm và trên cơ sở hàng ngày.

phân vùng

nhập mô tả hình ảnh ở đây

Ngoài ra, bảng được lập chỉ mục tốt bởi PK, số liệu thống kê được cập nhật và INDEXer bị phân mảnh mỗi đêm.

CHỌN dựa trên chỉ mục rất nhanh và chúng tôi không có vấn đề gì với nó.

Vấn đề

Tôi cần biết hàng cuối cùng (TOP) [End]và được phân vùng bởi [SourceDeciveID]. Để có được cuối cùng [OperationData]của mọi thiết bị nguồn.

Câu hỏi

Tôi cần tìm cách giải quyết vấn đề này một cách tốt và không đưa DB đến giới hạn.


Nỗ lực 1

Lần thử đầu tiên là hiển nhiên GROUP BYhoặc SELECT OVER PARTITION BYtruy vấn. Vấn đề ở đây cũng rất rõ ràng, mọi truy vấn đều phải quét theo thứ tự phân vùng / tìm hàng trên cùng. Vì vậy, truy vấn rất chậm và có tác động IO rất cao.

Ví dụ truy vấn 1

;WITH cte AS
(
   SELECT *,
         ROW_NUMBER() OVER (PARTITION BY [SourceDeciveID] ORDER BY [End] DESC) AS rn
   FROM [data].[OperationData]
)
SELECT *
FROM cte
WHERE rn = 1

Ví dụ truy vấn 2

SELECT *
FROM [data].[OperationData] AS d 
CROSS APPLY 
(
   SELECT TOP 1 *
   FROM [data].[OperationData] 
   WHERE [SourceDeciveID] = d.[SourceDeciveID]
   ORDER BY [End] DESC
) AS ds

THẤT ​​BẠI!

Nỗ lực 2

Tôi đã tạo một bảng trợ giúp để luôn giữ một tham chiếu đến hàng TOP.

CREATE TABLE [data].[LastOperationData](
    [SourceDeciveID] [bigint] NOT NULL,
    [FileSource] [nvarchar](256) NOT NULL,
    [End] [datetime2](7) NOT NULL,
 CONSTRAINT [PK_LastOperationData] PRIMARY KEY CLUSTERED 
(
    [SourceDeciveID] ASC
)

ALTER TABLE [data].[LastOperationData]  WITH CHECK ADD  CONSTRAINT [FK_LastOperationData_OperationData] FOREIGN KEY([SourceDeciveID], [FileSource], [End])
REFERENCES [data].[OperationData] ([SourceDeciveID], [FileSource], [End])

Để lấp đầy bảng, một trình kích hoạt đã tạo để luôn thêm / cập nhật hàng nguồn nếu [End]cột cao hơn được chèn.

CREATE TRIGGER [data].[OperationData_Last]
   ON  [data].[OperationData]
   AFTER INSERT
AS 
BEGIN
    SET NOCOUNT ON;

    MERGE [data].[LastOperationData] AS [target]
    USING (SELECT [SourceDeciveID], [FileSource], [End] FROM inserted) AS [source] ([SourceDeciveID], [FileSource], [End])  
    ON ([target].[SourceDeciveID] = [FileSource].[SourceDeciveID])

    WHEN MATCHED AND [target].[End] < [source].[End] THEN
        UPDATE SET [target].[FileSource] = source.[FileSource], [target].[End] = source.[End]

    WHEN NOT MATCHED THEN  
        INSERT ([SourceDeciveID], [FileSource], [End])  
        VALUES (source.[SourceDeciveID], source.[FileSource], source.[End]);

END

Vấn đề ở đây là, nó cũng có tác động IO rất lớn và tôi không biết tại sao.

Như bạn có thể thấy ở đây trong kế hoạch truy vấn, nó cũng thực hiện quét toàn bộ [OperationData]bảng.

Nó có tác động tổng thể rất lớn đến DB của tôi. số liệu thống kê

THẤT ​​BẠI!


2
Trong khối mã đầu tiên của bạn, tôi không thể thấy cột đầu tiên của chỉ mục được nhóm đến từ đâu - có đúng không?
George.Palacios

Có xin lỗi SSMS không bao gồm nó vào CREATE TABLEtập lệnh nhưng bên trong kế hoạch truy vấn bạn sẽ thấy các phân vùng. Tôi sẽ chỉnh sửa câu hỏi.
Steffen Mangold

Không phải là một chỉ số bổ sung bởi vì bao gồm bên trong PRIMARY KEY CLUSTEREDbạn nghĩ rằng nó có thể giúp đỡ?
Steffen Mangold

Soryy đó là một lỗi, tôi đã sửa đổi tên cho câu hỏi thành rõ ràng hơn, tôi đã sửa nó.
Steffen Mangold

@ ypercubeᵀᴹ có vì SELECT [SourceID], [Source], [End] FROM insertedmột số cách quét bảng trên [OperationData].
Steffen Mangold

Câu trả lời:


9

Nếu bạn có một bảng các SourceIDgiá trị và một chỉ mục trên bảng chính của bạn (SourceID, End) include (othercolumns), chỉ cần sử dụng OUTER APPLY.

SELECT d.*
FROM dbo.Sources s
OUTER APPLY (SELECT TOP (1) *
    FROM data.OperationData d
    WHERE d.SourceID = s.SourceID
    ORDER BY d.[End] DESC) d;

Nếu bạn biết bạn chỉ sau phân vùng mới nhất của mình, bạn có thể bao gồm bộ lọc trên Kết thúc, như AND d.[End] > DATEADD(day, -1, GETDATE())

Chỉnh sửa: Vì chỉ mục được nhóm của bạn được bật SourceID, Source, End), hãy đặt Nguồn vào bảng Nguồn của bạn và cũng tham gia vào đó. Sau đó, bạn không cần chỉ mục mới.

SELECT d.*
FROM dbo.Sources s -- Small table
OUTER APPLY (SELECT TOP (1) *
    FROM data.OperationData d -- Big table quick seeks
    WHERE d.SourceID = s.SourceID
    AND d.Source = s.Source
    AND d.[End] > DATEADD(day, -1, GETDATE()) -- If you’re partitioning on [End], do this for partition elimination
    ORDER BY d.[End] DESC) d;

Các chỉ số thực sự tăng tốc truy vấn. Một vấn đề thứ hai xảy ra với nó là một chỉ số không liên kết trên một bảng lớn như vậy gần như không thể nhận ra. Trên tất cả các bảng "dữ liệu lớn" của chúng tôi, chúng tôi làm việc với bộ chỉ mục được phân vùng. Chúng có thể được duy trì phân vùng trực tuyến theo phân vùng. Ngay khi bộ chỉ mục được phân vùng, vấn đề là cái cũ vì anh ta phải chạy qua mọi phân vùng.
Steffen Mangold

1
@SteffenMangold: Càng ít dữ liệu trong một chỉ mục càng tốt (miễn là nó có mọi thứ bạn cần) và loại trừ các chế độ xem được cụ thể hóa, chỉ mục được phân cụm có lượng dữ liệu tối đa có thể. Các chỉ mục được nhóm có mặt vì nhận được tất cả dữ liệu theo khóa là chuẩn. Trong trường hợp này, bạn đang nhận được tất cả dữ liệu, nhưng bạn không thực sự lấy nó bằng khóa, bạn sẽ lấy nó bằng một phần của khóa. Bạn cần một chỉ mục có thể được truy vấn với một phần của khóa.
jmoreno

Tôi thực sự xin lỗi nhưng có một SourceBảng tham chiếu sourceIDcột. Nguồn cột chỉ là một tên tệp. Đó là một chút đặt tên khó hiểu. Đối với mỗi Sourcethiết bị (sourceID), chỉ có thể có một mục nhập duy nhất cho một tệp source(cột) tại một dấu thời gian. Ngoài ra tôi không thể loại bỏ phân vùng vì mới nhất Endlà phân mảnh rộng rãi. Đó là lý do tại sao tôi đưa ra giải pháp kích hoạt. Tôi nghĩ rằng một truy vấn trực tiếp sẽ không hoạt động ở đây.
Steffen Mangold

@Rob Farley Tôi đã chỉnh sửa câu hỏi để rõ ràng hơn
Steffen Mangold

Với phân vùng, bạn sẽ thấy tất cả những người tìm kiếm vào từng phân vùng. Với vị ngữ phụ, bạn có thể làm cho nó không làm phiền với tất cả chúng, và chỉ một số. Làm cho nó một tháng nếu bạn cần.
Rob Farley
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.