Giao dịch hiệu quả, ghi khóa


5

Tôi đã có một quy trình được lưu trữ, trong đó chọn 1 bản ghi lại. thủ tục lưu sẵn có thể được gọi từ nhiều ứng dụng khác nhau trên các máy tính khác nhau. Ý tưởng là thủ tục được lưu trữ mang lại bản ghi tiếp theo cần được xử lý và nếu hai ứng dụng gọi là proc được lưu trữ cùng một lúc, cùng một bản ghi sẽ không được đưa trở lại. Truy vấn của tôi là dưới đây, tôi đang cố gắng để viết truy vấn một cách hiệu quả nhất có thể (sql 2008). Nó có thể được thực hiện hiệu quả hơn thế này không?

CREATE PROCEDURE GetNextUnprocessedRecord 
AS 
BEGIN 
    SET NOCOUNT ON; 

    --ID of record we want to select back 
    DECLARE @iID BIGINT  

    -- Find the next processable record, and mark it as dispatched 
    -- Must be done in a transaction to ensure no other query can get 
    -- this record between the read and update 
    BEGIN TRAN 

     SELECT TOP 1 
      @iID = [ID] 
     FROM 
      --Don't read locked records, only lock the specific record 
      [MyRecords] WITH (READPAST, ROWLOCK) 
     WHERE 
      [Dispatched] is null 
     ORDER BY 
      [Received] 

     --Mark record as picked up for processing 
     UPDATE 
      [MyRecords] 
     SET 
      [Dispatched] = GETDATE() 
     WHERE 
      [ID] = @iID  

    COMMIT TRAN 

    --Select back the specific record 
    SELECT 
     [ID], 
     [Data] 
    FROM  
     [MyRecords] WITH (NOLOCK, READPAST) 
    WHERE 
     [ID] = @iID 

END 
  0

Tôi không thuyết phục TSQL đây là transactionally an toàn ... 22 feb. 092009-02-22 08:07:45

  0

thử đặt một WAITFOR DELAY '0: 2: 0' sau khi SELECT và trước khi UPDATE, chạy SP và thực thi cùng một SP từ một kết nối khác ... 22 feb. 092009-02-22 08:17:24

  0

thực sự, tôi sai! HOLDLOCK có cùng tác dụng như REPEATABLEREAD trên bảng MyRecords. 22 feb. 092009-02-22 08:22:27

  0

Tốt hơn là an toàn theo mặc định. Nếu không có sự hỗn loạn khái niệm rộng rãi trên lỏng lẻo. 22 feb. 092009-02-22 08:43:46

  0

Tôi vẫn nghĩ rằng bạn sẽ cần phải thực hiện giao dịch SERIALIZABLE thay vì REPEATABLEREAD 22 feb. 092009-02-22 08:55:17

3

Sử dụng gợi ý khóa READPAST là chính xác và SQL của bạn có vẻ OK.

Tôi muốn thêm sử dụng XLOCK mặc dù đó cũng là HOLDLOCK/SERIALIZABLE

... 
[MyRecords] WITH (READPAST, ROWLOCK, XLOCK) 
... 

Điều này có nghĩa bạn sẽ có được ID, và độc quyền khóa hàng đó trong khi bạn tiếp tục và cập nhật nó.

Chỉnh sửa: thêm chỉ mục vào các cột Đã gửi và Đã nhận để làm cho nó nhanh hơn. Nếu [ID] (tôi cho rằng đó là PK) không được nhóm lại, INCLUDE [ID]. Và lọc các chỉ số quá bởi vì nó là SQL 2008

Bạn cũng có thể sử dụng cấu trúc này mà sẽ làm tất cả trong một đi mà không XLOCK hoặc HOLDLOCK

UPDATE 
    MyRecords 
SET 
    --record the row ID 
    @id = [ID], 
    --flag doing stuff 
    [Dispatched] = GETDATE() 
WHERE 
    [ID] = (SELECT TOP 1 [ID] FROM MyRecords WITH (ROWLOCK, READPAST) WHERE Dispatched IS NULL ORDER BY Received) 

UPDATE, assign, set in one

  0

Mẹo tuyệt vời! Tôi đã thêm gợi ý XLOCK và tạo ra một chỉ số không nhóm trên Received và Dispatched, và bao gồm ID với một bộ lọc "Dispatched IS NULL". Đây là thêm vào chỉ số nhóm của tôi trên ID. Tôi vẫn đang cố gắng tìm hiểu xem việc ấn định Cập Nhật được đặt trong một có nhanh hơn hay không. 23 feb. 092009-02-23 07:07:14

+1

Cảm ơn bạn. Tôi sẽ lo lắng về tính toàn vẹn giao dịch hơn hiệu suất hoàn toàn. Câu lệnh đơn sẽ loại bỏ sự cần thiết của XLOCK. 23 feb. 092009-02-23 07:48:33


0

Bạn có thể chỉ định mỗi trình chọn một id duy nhất và thêm cột pickerproc và pickstate vào hồ sơ của bạn. Sau đó

CẬP NHẬT MyRecords
SET pickerproc = myproc,
pickstate = 'tôi' - cho 'quá trình I'n
ĐÂU Id = (SELECT MAX (Id) TỪ MyRecords ĐÂU pickstate = 'A') - 'A'vailable

Điều đó giúp bạn ghi lại trong một bước nguyên tử và bạn có thể thực hiện phần còn lại của quá trình giải trí khi rảnh rỗi. Sau đó, bạn có thể đặt pickstate thành 'C'omplete', 'E'rror, hoặc bất cứ khi nào nó được giải quyết.

Tôi nghĩ Mitch đang đề cập đến một kỹ thuật tốt khác nơi bạn tạo bảng xếp hàng đợi và chèn Id vào đó. Có một số chủ đề SO - tìm kiếm 'bảng xếp hàng thông điệp'.

  0

mà chắc chắn không an toàn! 22 feb. 092009-02-22 08:51:33

  0

Và tại sao lại như vậy? Nó sẽ không đưa ra cùng một MAX (Id) với pickstate 'A' cho đến sau khi bạn đã thay đổi nó. Ít nhất nó không có trong kinh nghiệm của tôi dựa trên có lẽ 10^6 + trường hợp. 22 feb. 092009-02-22 08:55:38

  0

.. và nó sẽ không hiệu quả hoặc là 22 feb. 092009-02-22 08:57:49

  0

Tôi đang nghe ... đối số của bạn là gì? Tôi luôn có thể tìm hiểu thêm về SQL ... 22 feb. 092009-02-22 09:00:29

  0

@le dorfier: để làm cho giao dịch nhất quán, nó sẽ cần phải được bao bọc trong một giao dịch SERIALIZABLE. Nếu không, có nguy cơ đọc lần đọc/đọc không lặp lại/đọc ma/ 22 feb. 092009-02-22 09:01:27

  0

trừ khi tôi bị nhầm lẫn, thao tác MAX sẽ quét hàng. Nếu đây là một cái bàn lớn, nó sẽ giữ một sự lôi cuốn mở trong một thời gian tương đối dài. 22 feb. 092009-02-22 09:03:12

  0

Tôi ghét phải tìm ra rằng tài liệu khóa turgid một lần nữa, nhưng tôi bắt đầu với giả định rằng ACID áp dụng theo mặc định. Bạn không đồng ý? 22 feb. 092009-02-22 09:03:40

  0

(Khi phản ánh), tôi thấy điểm của bạn (ví dụ: bản số thấp của cột chọn lọc). Nhưng nó được giải quyết bằng "chỉ mục được lọc" có sẵn trong SQL 2008. Xem http://blog.sqlauthority.com/2008/09/01/sql-server-2008-introduction-to-filtered-index-improve-performance-with -filtered-index/ 22 feb. 092009-02-22 09:18:22


0

Bạn có thể giữ MyRecords trên bảng "MEMORY" để xử lý nhanh hơn.

  0

Bảng có thể nhận được khá lớn, cộng thêm, điều gì xảy ra nếu máy chủ bị treo? 22 feb. 092009-02-22 19:27:06