2011-01-18 46 views
5

Xét bảng sauLàm cách nào để thiết lập khóa cho thao tác đọc-thao tác ghi?

Key(KeyId int, Sequence varchar(14))

Giá trị chuỗi là một phong tục auto key increment kết hợp chữ cái và con số mà một nhu cầu khách hàng cụ thể cho hệ thống của mình.

Chúng tôi đã thực hiện một hàm gọi là GetNextSequence() sẽ trả về giá trị tiếp theo của chuỗi. Các bước để đọc và cập nhật các trình tự đi như sau

  1. Đọc giá trị chuỗi bằng cách sử dụng KeyId: SELECT Sequence FROM [Key] WHERE KeyId = @Id
  2. Phân tích các giá trị chuỗi và xác định giá trị tiếp theo
  3. Viết giá trị chuỗi để bàn: UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id

đây là mã C# (giản thể cho rõ ràng):

var transaction = connection.BeginTransaction(IsolationLevel.RepeatableRead); 
var currentSequenceValue = SqlUtils.ExecuteScalar(connection, transaction, "SELECT Sequence FROM [Key] WHERE KeyId = @Id", new SqlParameter("@Id", keyId)); 
var updatedSequenceValue = ParseSequence(currentSequenceValue); 
SqlUtils.ExecuteScalar(connection, transaction, "UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id", new SqlParameter("@Id", keyId), new SqlParameter("@Sequence", updatedSequenceValue)); 
transaction.Commit(); 
return updatedSequenceValue; 

vấn đề của chúng tôi cư trú tại đó hai máy chủ khác nhau có thể truy cập vào cùng một chuỗi và chúng tôi sẽ nhận được một bế tắc

giao dịch (Process ID X) đã bế tắc về tài nguyên khóa với quá trình khác và đã được chọn làm nạn nhân bế tắc. Chạy lại giao dịch.

Trong C#, tôi đã cố gắng để thiết lập kết hợp khóa khác nhau như một sự cô lập giao dịch IsolationLevel.RepeatableRead hoặc IsolationLevel.Serializable hoặc trong SQL sử dụng bảng gợi ý ROWLOCKHOLDLOCK, nhưng không thành công.

Tôi muốn mỗi máy chủ có thể đọc, thao tác và cập nhật chuỗi theo cách nguyên tử. Cách thích hợp để thiết lập khóa cho tình huống này là gì?

+0

trên một mặt lưu ý điều này nghe có vẻ như vấn đề này: http://www.codinghorror.com/blog/2008/08/deadlocked.html – BrokenGlass

Trả lời

1

Tôi khuyên bạn nên sử dụng khóa cấp hàng độc quyền trong thời gian giao dịch (ROWLOCK, XLOCK, HOLDLOCK). Việc sử dụng gợi ý của bạn cho đến nay vẫn chưa đủ.

BEGIN TRAN 
    SELECT Sequence FROM [Key] WITH (ROWLOCK, XLOCK, HOLDLOCK) WHERE KeyId = @Id 

    Parse the sequence value and determine the next value 

    UPDATE [Key] SET Sequence = @Sequence WHERE KeyId = @Id 
COMMIT 

Mặc dù, tôi muốn nhìn vào ít nhất giảm phạm vi để một giao dịch duy nhất

UPDATE [Key] WITH (ROWLOCK, XLOCK, HOLDLOCK) 
    SET Sequence = dbo.scalarudf(...) 
    WHERE KeyId = @Id 

Edit: bạn không cần HOLDLOCK nếu bạn sử dụng SERIALIZABLE. Và "RepeatableRead" có thể không đủ vì phạm vi bị khóa

+0

Ồ, tôi phải xác định rằng "Chuỗi phân tích cú pháp" được thực hiện trong C# và không trong SQL Server. –

+1

@ Pierre-Alain Vigeant: bạn vẫn cần các gợi ý và giao dịch. Bạn có thể di chuyển C# đến một fucntion CLR hoặc vào SQL? – gbn

+0

Dường như nó hoạt động với 'ROWLOCK, XLOCK, HOLDLOCK', nhưng tôi cần thử nghiệm thêm mà tôi sẽ làm vào ngày mai. –

2

Vấn đề là các khóa mặc định được mua để đọc không tránh điều kiện chủng tộc, vì có thể có nhiều ổ khóa đọc trên cùng một bản ghi.

Tình huống là, quy trình A mua lại khóa Đọc trên hàng X. Quy trình B sau đó mua lại khóa đọc trong khi A đang làm việc trên "phía máy khách" (trong chương trình máy chủ). Sau đó, yêu cầu nâng cấp lên khóa Viết trong khi B đang làm việc ở phía máy khách, tại thời điểm đó, nó được yêu cầu đợi cho đến khi khóa đọc của B được giải phóng. B sau đó yêu cầu một khóa viết và được chờ đợi cho đến khi A phát hành Đọc của nó. Cả hai đều đang chờ đợi khác để họ có thể có được khóa Viết độc quyền hơn.

Giải pháp là khóa độc quyền; bạn có thể chỉ định điều này bằng cách sử dụng gợi ý XLOCK.Một khóa độc quyền về cơ bản là một khóa cấp độ được mua để đọc, và được sử dụng trong trường hợp chính xác này, nơi bạn mong muốn viết một cái gì đó bạn đang đọc. Như đã lưu ý trong các ý kiến, một khóa độc quyền chỉ được duy trì nếu câu lệnh được thực hiện trong phạm vi giao dịch rõ ràng, vì vậy hãy đảm bảo bạn đang thiết lập một trong "đơn vị công việc" đang đọc giá trị, xác định cách tiến hành nó, và sau đó cập nhật nó.

Tôi sẽ sử dụng điều này ở cấp độ hàng (ROWLOCK), trừ khi bạn đang cập nhật nhiều chuỗi tương tự cùng một lúc; có được một trang hoặc khóa độc quyền cấp bảng làm cho mọi người chờ đợi dữ liệu mà bạn không cần nếu bạn chỉ làm việc trên một hàng cho mỗi giao dịch.

+0

XLOCK chỉ tồn tại cho câu lệnh hiện tại trừ khi có giao dịch rõ ràng. Nó không phải là đủ của chính nó. Vì vậy, nếu được sử dụng cho SELECT, bạn vẫn có thể bế tắc trên UPDATE. – gbn

+0

Tôi đã giả định một giao dịch rõ ràng; hàm GetNextSequence() là, từ từ đi, một "đơn vị công việc" nguyên tử. – KeithS

+0

Đúng, nhưng phải rõ ràng hơn về sự kiên trì của khóa độc quyền – gbn