Gợi ý Tablock gây ra bế tắc


10

Tôi đã chèn hai tập dữ liệu, sử dụng ghi nhật ký tối thiểu, vào một bảng heap trống bằng cách sử dụng thông qua hai Nhiệm vụ SQL thực thi chạy song song và với SQL có dạng sau.

INSERT INTO Table (TABLOCK) SELECT FROM ...

Sau khi công việc bị treo một chút, một trong các tác vụ SQL đã trở thành nạn nhân bế tắc. Dưới đây là đầu ra XML của biểu đồ khóa chết.

Ai đó có thể giải thích những gì đã xảy ra dưới mui xe?

  <resource-list>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process9609dc8" mode="Sch-S"/>
     <owner id="process9609dc8" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process5e13048" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
   <objectlock lockPartition="0" objid="1586156746" subresource="FULL" dbid="7" objectname="dbo.TargetTable" id="lock7374a00" mode="IX" associatedObjectId="1586156746">
    <owner-list>
     <owner id="process5e13048" mode="Sch-S"/>
     <owner id="process5e13048" mode="IX"/>
    </owner-list>
    <waiter-list>
     <waiter id="process9609dc8" mode="X" requestType="convert"/>
    </waiter-list>
   </objectlock>
  </resource-list>

Mọi thứ trở nên phức tạp hơn nhiều vì tôi thấy rằng trong hầu hết các trường hợp, hai Nhiệm vụ SQL thực thi có thể chạy song song thành công. Hãy thử dưới đây:

Create table dbo.TablockInsert (c1 int, c2 int, c3 int)

--then issue the script in two Execute Sql Task in parallel you won't fail:
insert into dbo.TablockInsert(TABLOCK) SELECT 1, 1, 1

Vì sự khác biệt duy nhất là câu lệnh CHỌN ... TỪ ..., có vẻ như câu lệnh CHỌN ... TỪ ... có thể có tác động đến chế độ khóa ở đây không?


Bạn có thể chỉ định TABLOCKX thay vì TABLOCK để ngăn chặn bế tắc. Mặc dù điều đó cũng sẽ tuần tự hóa quyền truy cập vào bảng, bạn vẫn sẽ nhận được ghi nhật ký tối thiểu.
Dan Guzman

Câu trả lời:


8

Các dữ liệu tải Performance Hướng dẫn được viết cho SQL Server 2008 nhưng như xa như tôi có thể nói Microsoft đã không thực hiện bất kỳ cải tiến trong lĩnh vực này cho đống. Đây là một trích dẫn cho kịch bản tải của bạn:

Tải số lượng lớn một bảng trống, không liên kết

Tải dữ liệu vào một bảng không liên kết, trong khi một thao tác đơn giản, có thể được tối ưu hóa theo nhiều cách.

...

Nhiều thao tác chèn đồng thời cho heaps chỉ có thể khi phương thức hàng loạt được chọn phát hành khóa cập nhật hàng loạt (BU) trên bảng. Hai khóa cập nhật hàng loạt (BU) tương thích và do đó hai hoạt động hàng loạt có thể chạy cùng một lúc.

Trong tình huống này, cả hai INSERT ... SELECT và SELECT INTO có một nhược điểm. Cả hai thao tác này đều thực hiện khóa độc quyền (X), khóa mức bảng trên đích. Điều này có nghĩa là chỉ một hoạt động tải số lượng lớn có thể chạy tại một thời điểm nhất định, hạn chế khả năng mở rộng. Tuy nhiên, BCP, BULK INSERT và Dịch vụ tích hợp đều có khả năng thực hiện các khóa cập nhật hàng loạt (BU) - nếu bạn chỉ định gợi ý TABLOCK.

Phần quan trọng là bạn không có khóa BU INSERT ... SELECT. Bạn sẽ luôn có một khóa độc quyền trên bàn, vì vậy chỉ có một khóa INSERTcó thể chạy cùng một lúc.

Trong các nhận xét bạn nói rằng bạn sẽ chèn 100 nghìn hàng hoặc ít hơn và các quy trình khác sẽ không chạy trên các bảng trong khi chèn. Khi gửi hai truy vấn INSERT đến cơ sở dữ liệu, tôi sẽ mong đợi một trong ba điều sẽ xảy ra:

  1. Một chèn chạy trước và chặn chèn khác. Chèn thứ hai chờ cho đến khi chèn đầu tiên được thực hiện.
  2. Một chèn kết thúc trước khi chèn thứ hai bắt đầu. Không có chặn rõ ràng nhưng chúng không chạy đồng thời.
  3. Bạn nhận được một bế tắc và chỉ có một chèn hoàn thành thành công.

Trong mọi trường hợp, bạn có lợi hoặc không bị tổn thương bằng cách thêm một TABLOCKXgợi ý vào truy vấn, vì vậy đó là khuyến nghị của tôi về cách giải quyết bế tắc. Nếu bạn muốn biết tại sao đôi khi bế tắc xảy ra, bạn cần tìm đến câu trả lời khác cho điều đó.

Đối với một kịch bản khác mà bạn thực sự cần chèn song song, hai cách giải quyết vấn đề BU là phân vùng heap của bạn và để mỗi phiên chèn vào một phân vùng riêng hoặc tải dữ liệu của bạn thông qua BCP, BULK INSERT hoặc Dịch vụ tích hợp .


Cảm ơn câu trả lời, nhưng tình huống tôi gặp bế tắc là trường hợp duy nhất tôi có bây giờ. Đối với hầu hết các trường hợp khi bạn phát hành XÁC NHẬN VÀO (TABLOCK) CHỌN TỪ song song, công việc sẽ không thành công. BTW, tôi đang sử dụng SQL SERVER 2008 R2. Tôi đã thêm một ví dụ thành công trong câu hỏi của tôi.
SqlWhale

@tec trong các trường hợp không thất bại có lẽ họ thực sự đã chạy ser seri. Có thể bạn chỉ gặp sự cố khi có thời gian khởi động nhất định, ví dụ như để biên soạn kế hoạch.
Martin Smith

@MartinSmith Truy vấn mà tôi gặp phải vấn đề trong dự án phức tạp hơn nhiều so với ví dụ CHỌN 1, yêu cầu nhiều chi phí biên dịch hơn nhưng nó chỉ đọc từ một vài bảng. Tôi đang cố gắng tái tạo loại bế tắc này bằng các truy vấn phức tạp hơn.
SqlWhale

@TecKnowNoth Về bạn có bao nhiêu hàng? Quá trình chạy bao nhiêu lần mỗi ngày? Các truy vấn khác CHỌN từ bảng trong khi dữ liệu được tải?
Joe Obbish

@JoeObbish 1. Tôi không nghĩ # số hàng là vấn đề ở đây, chúng tôi chỉ có 4000 - 70000 cho mỗi truy vấn và chúng là dữ liệu tổng hợp cao cho việc sử dụng khối. 2. Nó vẫn đang trong giai đoạn thử nghiệm, được cho là chạy một lần mỗi ngày. 3.Không khác là đọc từ bảng mục tiêu.
SqlWhale

4

Bạn đang chèn vào dbo.TargetTabletừ hai phiên và cả hai sử dụng TABLOCKhint.Both process9609dc8process5e13048quá trình tổ chức Sch-SIXổ khóa tương thích với nhau vì vậy cả hai quá trình có thể giữ cùng một lúc. Nhưng cả hai đều muốn chuyển đổi IXkhóa để Exclusive Xgõ. Xổ khóa không tương thích với nhau. Do đó, máy chủ SQL đã chọn một trong các phiên làm nạn nhân bế tắc thay vì chờ đợi vô hạn cho nhau.

Thông tin bế tắc cơ bản.

Biểu đồ khóa tương thích (công cụ cơ sở dữ liệu).

Phát hiện và kết thúc bế tắc.

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.