Chỉ số phân mảnh trong khi liên tục xử lý


10

Máy chủ SQL 2005

Tôi cần có thể liên tục xử lý khoảng 350 triệu bản ghi trong bảng bản ghi 900M. Truy vấn tôi đang sử dụng để chọn các bản ghi để xử lý trở nên bị phân mảnh nghiêm trọng khi tôi xử lý và tôi có nhu cầu dừng xử lý để xây dựng lại chỉ mục. Mô hình dữ liệu giả & truy vấn ...

/**************************************/
CREATE TABLE [Table] 
(
    [PrimaryKeyId] [INT] IDENTITY(1,1) NOT NULL PRIMARY KEY CLUSTERED,
    [ForeignKeyId] [INT] NOT NULL,
    /* more columns ... */
    [DataType] [CHAR](1) NOT NULL,
    [DataStatus] [DATETIME] NULL,
    [ProcessDate] [DATETIME] NOT NULL,
    [ProcessThreadId] VARCHAR (100) NULL
);

CREATE NONCLUSTERED INDEX [Idx] ON [Table] 
(
    [DataType],
    [DataStatus],
    [ProcessDate],
    [ProcessThreadId]
);
/**************************************/

/**************************************/
WITH cte AS (
    SELECT TOP (@BatchSize) [PrimaryKeyId], [ProcessThreadId]
    FROM [Table] WITH ( ROWLOCK, UPDLOCK, READPAST )
    WHERE [DataType] = 'X'
    AND [DataStatus] IS NULL
    AND [ProcessDate] < DATEADD(m, -2, GETDATE()) -- older than 2 months
    AND [ProcessThreadId] IS NULL
)
UPDATE cte
SET [ProcessThreadId] = @ProcessThreadId;

SELECT * FROM [Table] WITH ( NOLOCK )
WHERE [ProcessThreadId] = @ProcessThreadId;
/**************************************/

Nội dung dữ liệu ...
Trong khi cột [Kiểu dữ liệu ] được nhập dưới dạng CHAR (1), khoảng 35% của tất cả các bản ghi bằng 'X' với phần còn lại bằng 'A'.
Chỉ có các bản ghi trong đó [DataType] bằng 'X', khoảng 10% sẽ có giá trị KHÔNG NULL [DataStatus].

Các cột [ProcessDate] và [ProcessThreadId] sẽ được cập nhật cho mỗi bản ghi được xử lý.
Cột [DataType] được cập nhật ('X' được thay đổi thành 'A') khoảng 10% thời gian.
Cột [DataStatus] được cập nhật ít hơn 1% thời gian.

Bây giờ giải pháp của tôi là chọn khóa chính của tất cả các bản ghi để xử lý vào một bảng xử lý riêng. Tôi xóa các khóa khi tôi xử lý chúng để các phân đoạn chỉ mục tôi xử lý ít hồ sơ hơn.

Tuy nhiên, điều này không phù hợp với quy trình công việc tôi muốn có để các dữ liệu này được xử lý liên tục, không có sự can thiệp thủ công và thời gian chết đáng kể. Tôi dự đoán thời gian chết trên cơ sở hàng quý cho các công việc vệ sinh. Nhưng bây giờ, không có bảng xử lý riêng biệt, tôi không thể xử lý thậm chí một nửa bộ dữ liệu mà không có sự phân mảnh trở nên tồi tệ đến mức bắt buộc phải dừng và xây dựng lại chỉ mục.

Bất kỳ khuyến nghị để lập chỉ mục hoặc một mô hình dữ liệu khác nhau? Có một mô hình tôi cần nghiên cứu?
Tôi có toàn quyền kiểm soát mô hình dữ liệu và phần mềm xử lý để không có gì ngoài bàn.


Một người cũng nghĩ: chỉ mục của bạn có vẻ không đúng thứ tự: nó nên được chọn nhiều nhất để ít chọn lọc nhất. Vậy ProcessThreadId, ProcessDate, DataStatus, DataType có lẽ?
gbn

Chúng tôi đã quảng cáo nó trong cuộc trò chuyện của chúng tôi. Câu hỏi rất hay. chat.stackexchange.com/rooms/179/the-heap
gbn

Tôi đã cập nhật truy vấn để thể hiện chính xác hơn các lựa chọn. Tôi nhiều chủ đề đồng thời chạy này. Tôi đã lưu ý đề xuất đặt hàng chọn lọc. Cảm ơn.
Chris Gallucci

@ChrisGallucci Hãy trò chuyện nếu bạn có thể ...
JNK

Câu trả lời:


4

Những gì bạn đang làm là bạn đang sử dụng một bảng như một hàng đợi. Cập nhật của bạn là phương pháp dequeue. Nhưng chỉ số cụm trên bảng là một lựa chọn kém cho một hàng đợi. Sử dụng các bảng như Hàng đợi thực sự áp đặt các yêu cầu khá nghiêm ngặt đối với thiết kế bảng. Chỉ số cụm của bạn phải là thứ tự dequeue, trong trường hợp này có khả năng ([DataType], [DataStatus], [ProcessDate]). Bạn có thể thực hiện các khóa chính là một nonclustered hạn chế. Bỏ chỉ mục không phân cụm Idx, vì khóa phân cụm có vai trò của nó.

Một phần quan trọng khác của câu đố là giữ cho kích thước hàng không đổi trong quá trình xử lý. Bạn đã khai báo ProcessThreadIdnhư là một VARCHAR(100)hàm ý hàng tăng lên và co lại khi được 'xử lý' vì giá trị trường thay đổi từ NULL thành không null. Mẫu tăng trưởng này trên hàng gây ra sự phân tách và phân mảnh trang. Tôi không thể tưởng tượng ID chủ đề là 'VARCHAR (100)'. Sử dụng một loại chiều dài cố định, có lẽ một INT.

Là một lưu ý phụ, bạn không cần phải thực hiện theo hai bước (CẬP NHẬT theo sau là CHỌN). Bạn có thể sử dụng mệnh đề OUTPUT, như được giải thích trong bài viết được liên kết ở trên:

/**************************************/
CREATE TABLE [Table] 
(
    [PrimaryKeyId] [INT] IDENTITY(1,1) NOT NULL PRIMARY KEY NONCLUSTERED,
    [ForeignKeyId] [INT] NOT NULL,
    /* more columns ... */
    [DataType] [CHAR](1) NOT NULL,
    [DataStatus] [DATETIME] NULL,
    [ProcessDate] [DATETIME] NOT NULL,
    [ProcessThreadId] INT NULL
);

CREATE CLUSTERED INDEX [Cdx] ON [Table] 
(
    [DataType],
    [DataStatus],
    [ProcessDate]
);
/**************************************/

declare @BatchSize int, @ProcessThreadId int;

/**************************************/
WITH cte AS (
    SELECT TOP (@BatchSize) [PrimaryKeyId], [ProcessThreadId] , ... more columns 
    FROM [Table] WITH ( ROWLOCK, UPDLOCK, READPAST )
    WHERE [DataType] = 'X'
    AND [DataStatus] IS NULL
    AND [ProcessDate] < DATEADD(m, -2, GETDATE()) -- older than 2 months
    AND [ProcessThreadId] IS NULL
)
UPDATE cte
SET [ProcessThreadId] = @ProcessThreadId
OUTPUT DELETED.[PrimaryKeyId] , ... more columns ;
/**************************************/

Ngoài ra, tôi sẽ xem xét chuyển các mục được xử lý thành công vào một bảng lưu trữ, khác nhau. Bạn muốn các bảng xếp hàng của bạn lơ lửng gần bằng không, bạn không muốn chúng phát triển khi chúng giữ lại 'lịch sử' từ các mục cũ không cần thiết. Bạn cũng có thể xem xét phân vùng bằng cách [ProcessDate]thay thế (ví dụ: một phân vùng hoạt động hiện tại đóng vai trò là hàng đợi và lưu các mục với NULL ProcessDate và một phân vùng khác cho mọi thứ không null. Hoặc nhiều phân vùng cho không null nếu bạn muốn triển khai hiệu quả xóa (chuyển ra) cho dữ liệu đã qua thời gian lưu giữ bắt buộc. Nếu mọi thứ trở nên nóng, bạn có thể phân vùng thêm bằng cách[DataType] nếu nó có đủ độ chọn lọc, nhưng thiết kế đó sẽ thực sự phức tạp vì nó yêu cầu phân vùng theo cột được tính toán bền vững (một cột tổng hợp gắn kết với nhau [DataType] và [TreatmentDate]).


3

Tôi sẽ bắt đầu bằng cách di chuyển ProcessDateProcessthreadidcác lĩnh vực sang một bảng khác.

Ngay bây giờ, mỗi hàng bạn chọn từ chỉ mục khá rộng này cũng cần được cập nhật.

Nếu bạn di chuyển hai trường đó sang một bảng khác, khối lượng cập nhật của bạn trên bảng chính sẽ bị cắt 90%, điều này sẽ đảm nhiệm phần lớn sự phân mảnh.

Bạn vẫn sẽ phân mảnh trong bảng MỚI, nhưng sẽ dễ quản lý hơn trên bảng hẹp hơn với ít dữ liệu hơn.


Việc phân tách dữ liệu này và vật lý dựa trên [Kiểu dữ liệu] sẽ đưa tôi đến nơi tôi cần. Tôi hiện đang trong giai đoạn thiết kế (thực sự thiết kế lại) về việc này vì vậy sẽ mất một thời gian trước khi tôi có cơ hội thử lái thay đổi này.
Chris Gallucci
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.