2009-11-30 28 views
6

Tôi đang viết một dịch vụ nền cần xử lý một loạt công việc, được lưu trữ dưới dạng bản ghi trong bảng sqlserver. Dịch vụ cần tìm 20 công việc lâu đời nhất cần được làm việc (where status = 'new'), đánh dấu chúng (set status = 'processing'), chạy chúng và cập nhật công việc sau đó.Đánh dấu nguyên tử và trả về một nhóm hàng trong cơ sở dữ liệu

Đó là phần đầu tiên tôi cần trợ giúp. Có thể có nhiều chuỗi truy cập cơ sở dữ liệu cùng một lúc và tôi muốn đảm bảo rằng truy vấn "đánh dấu & trả về" chạy nguyên tử hoặc gần như vậy.

Dịch vụ này sẽ dành ít thời gian truy cập cơ sở dữ liệu và không phải là kết thúc của thế giới nếu công việc được chạy hai lần, vì vậy tôi có thể chấp nhận một xác suất nhỏ của công việc chạy nhiều lần trong mã.

Cách tốt nhất để làm điều này là gì? Tôi đang sử dụng linq-to-sql cho lớp dữ liệu của tôi, nhưng tôi giả sử tôi sẽ phải thả xuống t-sql cho việc này.

Trả lời

10

bảng của bạn công việc là hàng đợi. Việc viết các hàng đợi của các bảng người dùng được sao lưu là một lỗi nổi tiếng vì nó dẫn đến các sự cố và các vấn đề đồng thời.

Điều đơn giản nhất là thả bảng người dùng và sử dụng đúng queue để thay thế. Điều này sẽ cung cấp cho bạn hàng đợi miễn phí sự cố đồng thời miễn phí trên hệ thống mã được kiểm tra và xác nhận hợp lệ. Vấn đề là toàn bộ mô hình xung quanh hàng đợi thay đổi từ INSERT và DELETE/UPDATE thành SEND/RECEIVE. Mặt khác với hàng đợi được tích hợp sẵn, bạn sẽ nhận được một số tiện ích miễn phí rất mạnh mẽ, cụ thể là Activationcorrelated items locking.

Nếu bạn muốn tiếp tục xuống con đường của bảng sử dụng hàng đợi sao lưu sau đó thứ hai lừa quan trọng nhất bằng văn bản hàng đợi sử dụng bảng là sử dụng CẬP NHẬT ... OUTPUT:

WITH cte AS (
    SELECT TOP(20) status, id, ... 
    FROM table WITH (ROWLOCK, READPAST, UPDLOCK) 
    WHERE status = 'new' 
    ORDER BY enqueue_time) 
UPDATE cte 
    SET status = 'processing' 
OUTPUT 
    INSERTED.id, ... 

Cú pháp CTE là chỉ để thuận tiện cho việc đặt TOP và ORDER BY đúng cách, truy vấn có thể được viết bằng cách sử dụng các bảng có nguồn gốc như một cách thuận tiện. Bạn không thể sử dụng UPDATE thẳng ...TOP vì UPDATE không hỗ trợ ORDER BY và bạn yêu cầu điều này để đáp ứng phần 'lâu đời nhất' theo yêu cầu của bạn. Các gợi ý khóa là cần thiết để tạo thuận lợi cho sự đồng nhất cao giữa các luồng xử lý song song.

Tôi đã nói đây là mẹo quan trọng thứ hai. Điều quan trọng nhất là cách bạn tổ chức bảng. Đối với hàng đợi, phải được nhóm theo (status, enqueue_time). Nếu bạn không tổ chức bảng đúng cách bạn sẽ kết thúc với deadlocks. Nhận xét trước emptive: phân mảnh là không thích hợp trong kịch bản này.

+0

Bạn có thể giải thích tại sao sẽ có deadlocks nếu bảng không được nhóm lại (trạng thái, enqueue_time) ngay cả sau khi sử dụng 3 gợi ý bạn quy định? –

+0

Tôi không biết về mệnh đề OUTPUT, cùng với các gợi ý tạo ra một giải pháp hoàn chỉnh. Điều này trả lời câu hỏi của riêng tôi tại SO. –

8

Vui lòng xem câu trả lời của tôi tại đây: SQL Server Process Queue Race Condition cũng quản lý 20 hàng trong một lần.

Về cơ bản, nó khá đơn giản trong SQL Server để quản lý đồng thời và bỏ phiếu bằng cách sử dụng các gợi ý ROWLOCK, READPAST và UPDLOCK.

Tôi không thể bình luận về LINQ, nhưng giao dịch vẫn còn để bạn mở đến vấn đề đồng thời: bạn cần phải sử dụng các gợi ý tôi đã đề cập

+0

Các bài viết khác của bạn rất hữu ích. Tôi đã bỏ lỡ một trong ba gợi ý. –

1

Tôi biết đó là off topic nhưng đối với điều này, bạn có thể sử dụng MSMQ. Một hàng đợi tin nhắn sẽ đặt công việc của bạn theo trình tự và chuỗi của nó an toàn. Bạn cũng có thể gán quyền ưu tiên MSMQ cho chính nó. Bạn có thể sử dụng tính năng đọc hoặc xem để xóa thư khỏi hàng đợi hoặc chỉ xem những gì ở đó. Bạn có thể sử dụng mẫu thiết kế lệnh để giúp bạn với điều này.

+0

Xếp hàng là câu trả lời, nhưng tại sao MSMQ khi SQL Server đi kèm với hàng đợi được xây dựng? –

+0

Cách tôi sử dụng chúng là kiểm soát các quy trình. Khi tôi xếp hàng một cái gì đó tôi không sử dụng cơ sở dữ liệu ở tất cả. Vì vậy, bất kỳ người nghe có thể có được một công việc để làm. Và tôi đã thử nghiệm nó với 5 máy tính chạy 10 tiến trình mỗi và tôi không bao giờ có một vấn đề đồng thời. Tôi đoán nó phụ thuộc vào nơi bạn muốn bạn xếp hàng để cư trú. –

0

Nó không chỉ đơn giản như chạy T-SQL của bạn trong một giao dịch, hoặc tôi thiếu một cái gì đó?

4

xây dựng trên gbn's answer ...

Nếu bạn đang sử dụng SQL Server 2005 hoặc mới hơn, bạn có thể trả lại hàng được cập nhật nguyên tử bằng cách sử dụng một OUTPUT clause trong UPDATE tuyên bố của bạn:

UPDATE TOP (20) your_table 
SET status = 'processing' 
OUTPUT INSERTED.* 
FROM your_table WITH (ROWLOCK, READPAST, UPDLOCK) 
WHERE status = 'new' 
Các vấn đề liên quan