2010-04-29 12 views
7

Tôi gặp sự cố với nhiều lần deadlocks trên máy chủ SQL 2005. Điều này nằm giữa câu lệnh INSERT và SELECT.Lỗi máy chủ SQL giữa câu lệnh INSERT và SELECT

Có hai bảng. Bảng 1 và Bảng 2. Bảng 2 có PK của Table1 (table1_id) là khóa ngoài.
Chỉ mục trên table1_id được nhóm lại.

INSERT chèn một hàng vào bảng2 cùng một lúc.
SELCET tham gia 2 bảng. (đó là một truy vấn dài có thể mất tối đa 12 giây để chạy)

Theo hiểu biết của tôi (và thử nghiệm) INSERT phải có khóa IS trên bảng 1 để kiểm tra tính toàn vẹn tham chiếu (không gây ra bế tắc). Nhưng, trong trường hợp này nó đã mua một trang IX khóa

Báo cáo bế tắc:

<deadlock-list> 
<deadlock victim="process968898"> 
    <process-list> 
    <process id="process8db1f8" taskpriority="0" logused="2424" waitresource="OBJECT: 5:789577851:0 " waittime="12390" ownerId="61831512" transactionname="user_transaction" lasttranstarted="2010-04-16T07:10:13.347" XDES="0x222a8250" lockMode="IX" schedulerid="1" kpid="3764" status="suspended" spid="52" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-04-16T07:10:13.350" lastbatchcompleted="2010-04-16T07:10:13.347" clientapp=".Net SqlClient Data Provider" hostname="VIDEV01-B-ME" hostpid="3040" loginname="DatabaseName" isolationlevel="read uncommitted (1)" xactid="61831512" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
    <executionStack> 
    <frame procname="DatabaseName.dbo.prcTable2_Insert" line="18" stmtstart="576" stmtend="1148" sqlhandle="0x0300050079e62d06e9307f000b9d00000100000000000000"> 
INSERT INTO dbo.Table2 
    (
     f1, 
     table1_id, 
     f2 
    ) 
    VALUES 
    (
     @p1, 
     @p_DocumentVersionID, 
     @p1 

    )  </frame> 
    </executionStack> 
    <inputbuf> 
Proc [Database Id = 5 Object Id = 103671417] </inputbuf> 
    </process> 
    <process id="process968898" taskpriority="0" logused="0" waitresource="PAGE: 5:1:46510" waittime="7625" ownerId="61831406" transactionname="INSERT" lasttranstarted="2010-04-16T07:10:12.717" XDES="0x418ec00" lockMode="S" schedulerid="2" kpid="1724" status="suspended" spid="53" sbid="0" ecid="0" priority="0" transcount="2" lastbatchstarted="2010-04-16T07:10:12.713" lastbatchcompleted="2010-04-16T07:10:12.713" clientapp=".Net SqlClient Data Provider" hostname="VIDEV01-B-ME" hostpid="3040" loginname="DatabaseName" isolationlevel="read committed (2)" xactid="61831406" currentdb="5" lockTimeout="4294967295" clientoption1="671088672" clientoption2="128056"> 
    <executionStack> 
    <frame procname="DatabaseName.dbo.prcGetList" line="64" stmtstart="3548" stmtend="11570" sqlhandle="0x03000500dbcec17e8d267f000b9d00000100000000000000"> 
     <!-- XXXXXXXXXXXXXX...SELECT STATEMENT WITH Multiple joins including both Table2 table 1 and .... XXXXXXXXXXXXXXX --> 
    </frame> 
    </executionStack> 
    <inputbuf> 
Proc [Database Id = 5 Object Id = 2126630619] </inputbuf> 
    </process> 
    </process-list> 
    <resource-list> 
    <pagelock fileid="1" pageid="46510" dbid="5" objectname="DatabaseName.dbo.table1" id="lock6236bc0" mode="IX" associatedObjectId="72057594042908672"> 
    <owner-list> 
    <owner id="process8db1f8" mode="IX"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="process968898" mode="S" requestType="wait"/> 
    </waiter-list> 
    </pagelock> 
    <objectlock lockPartition="0" objid="789577851" subresource="FULL" dbid="5" objectname="DatabaseName.dbo.Table2" id="lock970a240" mode="S" associatedObjectId="789577851"> 
    <owner-list> 
    <owner id="process968898" mode="S"/> 
    </owner-list> 
    <waiter-list> 
    <waiter id="process8db1f8" mode="IX" requestType="wait"/> 
    </waiter-list> 
    </objectlock> 
    </resource-list> 
</deadlock> 
</deadlock-list> 

bất cứ ai có thể giải thích lý do tại sao INSERT được khóa trang IX?
Tôi không đọc báo cáo bế tắc đúng cách?
BTW, tôi chưa quản lý được sự cố này.

Cảm ơn!

EDIT: BẢNG TẠO:

CREATE TABLE [dbo].[Table2] (
    [Table2_id] [int] IDENTITY (1, 1) NOT NULL , 
    [f1] [int] NULL , 
    [Table1_id] [int] NOT NULL , 
    [f2] [int] NOT NULL , 
) 

ALTER TABLE [dbo].[Table2] ADD 
    CONSTRAINT [FK_Table2_Table1] FOREIGN KEY 
    (
     [Table1_id] 
    ) REFERENCES [dbo].[Table1] (
     [Table1_id] 
    ) 


CREATE TABLE [dbo].[Table1] (
    [Table1_id] [int] IDENTITY (1, 1) NOT NULL , 
) 

ALTER TABLE [dbo].[Table1] WITH NOCHECK ADD 
    CONSTRAINT [PK_Table1] PRIMARY KEY CLUSTERED 
    (
     [Table1_id] 
    ) 
+0

Bạn có thể thêm các câu lệnh DDL cho các bảng và khóa ngoại không? –

Trả lời

2

Tại một punt, tôi muốn nói rằng DatabaseName.dbo.prcTable2_Insert được thực hiện bên trong một giao dịch hoặc mở một cách rõ ràng, và nó (hoặc kết nối với mở giao dịch) đã thực hiện chèn trước vào Bảng 1.

+0

Tôi nghĩ bạn đã giúp tôi đi đúng hướng ở đây. Tôi đã tìm thấy một giao dịch với một bản cập nhật cho TABLE1 chỉ SAU cuộc gọi đến chèn. Thứ tự khóa vẫn không có ý nghĩa với tôi. Tại sao nó lại có khóa IX trước khi nó cần? không nên nó leo thang khóa sau khi chèn đã được thực hiện? – dtroy

+0

BTW, tôi vẫn không thể tự mình tái tạo. Tôi đã tạo ra một giao dịch với hai insets/updates, chạy trong một vòng lặp song song với việc chạy một vòng lặp với lệnh SELECT. Bất kỳ ý tưởng? – dtroy

+0

@dtroy - Tôi e là tôi không có nhiều đề xuất hơn nữa, không có khả năng tái sản xuất (và dĩ nhiên, nếu bạn quản lý nó, tôi nghi ngờ bạn cũng cần trợ giúp thêm ở đây). –

6

I có nghĩa là khóa 'ý định' và chúng luôn được liên kết với cấu trúc phân cấp. Bởi vì trình quản lý khóa không hiểu cấu trúc vật lý, nên không thể để anh ta tôn trọng các khóa phân cấp, do đó cấu trúc phân cấp được tạo lại trong các khóa dự định.

Trong trường hợp của bạn, INSERT có khóa cố định trên một trang. Điều này ngụ ý nó cũng đã thu được một khóa X trên một hàng trong trang, đó là hành vi bình thường. Nó bây giờ cố gắng để có được một khóa IX mới, vì vậy nó có thể cần phải chèn một hàng trong một trang khác nhau. Đây sẽ là hành vi bình thường của một chèn vào một bảng với nhiều chỉ mục: đầu tiên IX là một trong các chỉ mục (có thể là nhóm) và IX thứ hai là trên một chỉ mục không nhóm.

SELECT bạn nói sẽ trả về trong 12 giây, do đó, truy vấn dài, trên tập dữ liệu lớn và kế hoạch có thể đã chọn mức chi tiết khóa cao, khóa trang. SELECT có khóa S trên trang mà INSERT muốn khóa IX và muốn một khóa S khác trên trang INSERT có khóa IX.

Đây là một bế tắc không đáng kể và phải rất dễ sửa chữa: đảm bảo rằng CHỌN của bạn không cần các trang S-khóa đó. Đây không phải là lỗi INSERT ở đây. Không biết SELECt làm gì tôi không thể nói chắc chắn cho dù là tối ưu hay không. Theo kinh nghiệm của tôi, hầu như luôn luôn một CHỌN như thế này có rất nhiều, rất nhiều và nhiều phòng hơn để cải thiện (eitehr SELECT chính nó hoặc lược đồ bên dưới nó).

Nhưng chấp nhận rằng SELECT là tối ưu, thẻ get-out-of-tù đơn giản nhất của bạn là bật row versioning:

ALTER DATABASE <dbname> SET ALLOW_SNAPSHOT_ISOLATION ON; 
ALTER DATABASE <dbname> SET READ_COMMITTED_SNAPSHOT ON; 

Cập nhật:

Trên thực tế trên thứ hai đọc là điều hiển nhiên rằng INSERT có khóa trên các bảng khác nhau (trừ khi bạn modfied XML, có vẻ như được chỉnh sửa ở đây và ở đó), do đó, giải thích của bạn về cách chèn hành vi phải là sai. INSERT là một phần của một giao dịch hơn ít nhất hai lần viết, một trên Bảng 1 và một trên Bảng 2. Nhưng điều này không thay đổi nhiều vấn đề cũng như giải pháp. Đúng là bạn có con đường tách hai người viết trong giao dịch thành các giao dịch riêng biệt, nhưng rõ ràng đó là đại lộ tồi tệ nhất.

+0

Chỉ nên có một lần ghi. Tôi đã sửa đổi xml để đơn giản hóa mọi thứ và loại bỏ một số tên nhạy cảm. – dtroy