Hiệu quả Bộ lọc lớn với các bất đồng


9

Hãy nói rằng tôi có một bàn duy nhất

CREATE TABLE Ticket (
    TicketId int NOT NULL,
    InsertDateTime datetime NOT NULL,
    SiteId int NOT NULL,
    StatusId tinyint NOT NULL,
    AssignedId int NULL,
    ReportedById int NOT NULL,
    CategoryId int NULL
);

Trong ví dụ TicketIdnày là Khóa chính.

Tôi muốn người dùng có thể tạo các truy vấn "một phần đặc biệt" đối với bảng này. Tôi nói một phần vì một vài phần của truy vấn sẽ luôn được sửa:

  1. Truy vấn sẽ luôn thực hiện một bộ lọc phạm vi trên một InsertDateTime
  2. Truy vấn sẽ luôn luôn ORDER BY InsertDateTime DESC
  3. Truy vấn sẽ kết quả trang

Người dùng có thể tùy chọn lọc trên bất kỳ cột nào khác. Họ có thể lọc trên không, một hoặc nhiều. Và đối với mỗi cột, người dùng có thể chọn từ một tập hợp các giá trị sẽ được áp dụng dưới dạng phân tách. Ví dụ:

SELECT
    TicketId
FROM (
    SELECT
        TicketId,
        ROW_NUMBER() OVER(ORDER BY InsertDateTime DESC) as RowNum
    FROM Ticket
    WHERE InsertDateTime >= '2013-01-01' AND InsertDateTime < '2013-02-01'
      AND StatusId IN (1,2,3)
      AND (CategoryId IN (10,11) OR CategoryId IS NULL)
    ) _
WHERE RowNum BETWEEN 1 AND 100;

Bây giờ giả sử bảng có 100.000.000 hàng.

Điều tốt nhất tôi có thể đưa ra là một chỉ số bao gồm từng cột "tùy chọn":

CREATE NONCLUSTERED INDEX IX_Ticket_Covering ON Ticket (
    InsertDateTime DESC
) INCLUDE (
    SiteId, StatusId, AssignedId, ReportedById, CategoryId
);

Điều này cho tôi một kế hoạch truy vấn như sau:

  • LỰA CHỌN
    • Bộ lọc
      • Hàng đầu
        • Dự án trình tự (Tính toán vô hướng)
          • Bộ phận
            • Tìm kiếm chỉ mục

Có vẻ khá tốt. Khoảng 80% -90% chi phí đến từ hoạt động Tìm kiếm Index, rất lý tưởng.

Có chiến lược tốt hơn để thực hiện loại tìm kiếm này?

Tôi không nhất thiết muốn giảm tải bộ lọc tùy chọn cho khách hàng vì trong một số trường hợp, kết quả được đặt từ phần "cố định" có thể là 100 hoặc 1000 giây. Sau đó, khách hàng cũng chịu trách nhiệm phân loại và phân trang có thể làm việc quá nhiều cho khách hàng.


Có thể đặt truy vấn con của bạn vào một bảng tạm thời hoặc biến bảng và xây dựng theo cách đó? Với các bảng lớn hơn của tôi, đôi khi tôi bị choáng bởi các truy vấn con. Chỉ số bao gồm chỉ đưa bạn đến nay.
Valkyrie

@Valkyrie có vẻ không hiệu quả. Cũng xem xét rằng các biến thể của truy vấn này (các tham số khác nhau và các tùy chọn khác nhau trong các mệnh đề) có thể sẽ thực thi nhiều lần trong một giây cả ngày và cần trả về kết quả trung bình trong vòng dưới 100ms. Chúng tôi đã làm điều này, và nó thực hiện tốt bây giờ. Tôi chỉ tìm kiếm ý tưởng về cách tiếp tục cải thiện hiệu suất cho khả năng mở rộng.
Joseph Daigle

Bao nhiêu bạn quan tâm về việc sử dụng không gian lưu trữ?
Jon Seigel

@JonSeigel tùy thuộc vào mức độ ... nhưng tôi muốn xem bất kỳ đề xuất nào
Joseph Daigle

2
Và cách tiếp cận / truy vấn của bạn để có được trang thứ 2 của kết quả là gì? RowNum BETWEEN 101 AND 200?
ypercubeᵀᴹ

Câu trả lời:


1

Nếu tải công việc cụ thể này là phần lớn các truy vấn đối với bảng bạn có thể xem xét:

ALTER TABLE Ticket ADD CONSTRAINT PK_Ticket PRIMARY KEY NONCLUSTERED (TicketId);

CREATE UNIQUE CLUSTERED INDEX IX_Ticket_Covering ON Ticket (
    InsertDateTime ASC
);

Cân nhắc:

  • bạn có thể sử dụng datetime2 (SQL 2008+; độ chính xác linh hoạt)
  • ChènDateTime sẽ là duy nhất trong độ chính xác của bạn
  • nếu thời gian không bị hạn chế, sql duy nhất sẽ thêm một cột duy nhất ẩn kiểu int. Điều này được thêm vào tất cả các chỉ mục không xác định để họ có thể tham chiếu bản ghi cụm chính xác

Ưu điểm:

  • Thêm hàng mới vào cuối bảng
  • ngăn việc viết các cột bộ lọc tùy chọn hai lần (một lần trong cụm và một lần trên lá chỉ mục cho bao gồm)
  • phần lớn thời gian của bạn vẫn sẽ nằm trên một chỉ mục cụm tìm kiếm với nhiều hoặc ít hơn các trình quay.
  • sau đó thêm chỉ mục không bao gồm khác cho hầu hết các cặp cột phổ biến

1

Tôi đã sử dụng kỹ thuật này trong quá khứ. Bảng không lớn lắm nhưng tiêu chí tìm kiếm phức tạp hơn.

Đây là phiên bản ngắn.

CREATE PROC usp_Search
    (
    @StartDate  Date,
    @EndDate    Date,
    @Sites      Varchar(30) = NULL,
    @Assigned   Int = NULL, --Assuming only value possible
    @StartRow   Int,
    @EndRow     Int
    )
AS
DECLARE @TblSites   TABLE (ID Int)
IF @Sites IS NOT NULL
BEGIN
    -- Split @Sites into table @TblSites
END
SELECT  TicketId
FROM    (
        SELECT  TicketId,
                ROW_NUMBER() OVER(ORDER BY InsertDateTime DESC) as RowNum
        FROM    Ticket
                LEFT JOIN @TblSites
                    Ticket.SiteID = @TblSites.ID
        WHERE   InsertDateTime >= @StartDate 
                AND InsertDateTime < @EndDate
                AND (
                    @Assigned IS NULL 
                    OR AssignedId = @Assigned 
                    )
        ) _
WHERE   RowNum BETWEEN @StartRow AND @EndRow;

1

Đưa ra hai điều kiện tiên quyết đầu tiên của bạn, tôi sẽ xem xét một chỉ số nhóm trên InsertDateTime.



-1

Nếu các máy khách đang lọc theo cách gần như lặp đi lặp lại, bạn có thể tạo một chỉ mục cho các truy vấn đó.

Ví dụ: máy khách đang lọc trên SiteId và StatusId, bạn có thể tạo một chỉ mục bổ sung:

CREATE NONCLUSTERED INDEX IX_Ticket_InsertDateTime_SiteId_StatusId ON Ticket     
(InsertDateTime DESC,
 SiteId [ASC/DESC],
 StatusId [ASC/DESC] ) 
 INCLUDE ( ... );

Bằng cách này, hầu hết các truy vấn 'phổ biến hơn' có thể chạy nhanh.

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.