Thursday, August 13, 2009

Cách giải quyết Concurrency và xung đột khi tạo khóa tự động trong SQL Server

Yêu cầu

Trong yêu cầu thực tế của dự án mà tôi gặp phải, đó là trong trường hợp tạo mã khách hàng trong dữ liệu khách hàng, chúng tôi cần phải đảm bảo các yêu cầu sau:

1. Mã khách hàng được sinh tự động gồm 13 ký tự số.

2. Mã khách hàng được đánh theo nguyên tắc: [xxx]{0}[Số thứ tự tăng dần]

Trong đó: [xxx] là 3 ký tự số quy định tùy theo nơi đăng ký sẽ khách nhau; {0} là được chèn vào để đảm bảo mã khách hàng luôn là 13 ký tự số; [Số thứ tự tăng dần] là số tăng dần bắt đầu từ 1.

Phân tích

Rõ ràng, với [Số thứ tự tăng dần] thì chúng tôi đơn giản tạo 1 cột số nguyên và sử dụng tính năng Auto-increment của SQL Server. Với mã khách hàng của khách hàng mới, thì sẽ thực hiện lần lượt các công việc:

1. SELECT [Số thứ tự tăng dần] từ bảng Customers

2. [Mã khách hàng mới] = [Số thứ tự tăng dần] + 1

2. Gán các số 0 vào đầu [Mã khách hàng mới] để đủ 10 ký tự số

3. Gán 3 ký tự số [xxx] cho sẳn vào đầu [Mã khách hàng mới]

4. INSERT [Mã khách hàng mới] vào cơ sở dữ liệu

Vậy, việc giải quyết vấn đề Concurrency và xung đột sẽ như thế nào cần được đặt ra.

Cách giải quyết vấn đề

Có một cách giải quyết vấn đề đơn giản và hiệu quả là sử dụng lời gợi ý (hint) UPDLOCK. Khóa UPDATE là một shared lock mà sẽ không khóa các lệnh đọc khác. Tuy nhiên, tại một thời điểm chỉ có một xử lý (process) được giữ UPDLOCK trên 1 dòng (row), vì vậy nếu đồng thời có 2 xử lý song song đồng thời, thì xử lý thứ hai sẽ phải chờ xử lý thứ nhất hoàn thành rồi mới tiến hành giữ khóa UPDATE để thực hiện.

Và đây là đoạn code để mô tả cách giải quyết vấn đề trên:

BEGIN TRANSACTION

SELECT coalesce(MAX(CustomerIndex, 0)) + 1 FROM Customers WITH (UPDLOCK)

-- Thực hiện tạo Mã khách hàng mới dựa trên số thứ tự nhận được

INSERT Customers(...)

COMMIT TRANSACTION

No comments:

Post a Comment