2009-03-04 26 views
24

Tôi có một ứng dụng có khả năng thực hiện hàng ngàn lần chèn vào cơ sở dữ liệu SQL Server 2005. Nếu một chèn không thành công vì bất kỳ lý do nào (ràng buộc khóa ngoài, độ dài trường, vv) ứng dụng được thiết kế để ghi lại lỗi chèn và tiếp tục.SqlTransaction đã hoàn tất

Mỗi lần chèn độc lập với các trình cắm khác, vì vậy các giao dịch không cần thiết cho tính toàn vẹn của cơ sở dữ liệu. Tuy nhiên, chúng tôi muốn sử dụng chúng để đạt được hiệu suất. Khi tôi sử dụng các giao dịch, chúng tôi sẽ nhận được lỗi sau vào khoảng 1 trong số 100 cam kết.

This SqlTransaction has completed; it is no longer usable. 
    at System.Data.SqlClient.SqlTransaction.ZombieCheck() 
    at System.Data.SqlClient.SqlTransaction.Commit() 

Để cố gắng theo dõi nguyên nhân tôi đặt dấu vết vào mọi hoạt động giao dịch để tôi có thể đảm bảo giao dịch không bị đóng trước khi gọi cam kết. Tôi đã xác nhận rằng ứng dụng của tôi không đóng giao dịch. Sau đó tôi chạy lại ứng dụng bằng cách sử dụng chính xác cùng một dữ liệu đầu vào và thành công.

Nếu tôi tắt ghi nhật ký, nó sẽ không thành công nữa. Bật lại và thành công. Điều này bật/tắt chuyển đổi được thực hiện thông qua app.config mà không cần phải biên dịch lại.

Rõ ràng là hành động ghi nhật ký thay đổi thời gian và làm cho nó hoạt động. Điều này sẽ chỉ ra một vấn đề luồng. Tuy nhiên, ứng dụng của tôi không phải là đa luồng.

Tôi đã nhìn thấy một mục MS KB cho biết lỗi có khuôn khổ .Net 2.0 có thể gây ra sự cố tương tự (http://support.microsoft.com/kb/912732). Tuy nhiên, sửa chữa họ cung cấp không giải quyết vấn đề này.

+2

Sử dụng giao dịch "để đạt được hiệu suất"? Việc sử dụng các giao dịch cải thiện hiệu suất như thế nào? – LukeH

+0

Đồng ý với luke, tại sao bạn thấy hoặc nghĩ rằng bạn có được hiệu suất bằng cách làm như vậy? – eglasius

+8

có giao dịch xung quanh nhiều lần chèn, nói chung, tăng hiệu suất, tự kiểm tra và xem. bằng cách có một giao dịch, bạn giảm số lượng khóa bạn cần mua trước khi chèn –

Trả lời

7

Khó giúp đỡ mà không nhìn thấy mã. Tôi giả định từ mô tả của bạn, bạn đang sử dụng một giao dịch để cam kết sau mỗi lần chèn N, điều này sẽ cải thiện hiệu suất so với cam kết mỗi chèn được cung cấp N không quá lớn.

Nhưng nhược điểm là: nếu chèn không thành công, bất kỳ chèn nào khác trong lô hiện tại của N sẽ được cuộn lại khi bạn quay lại giao dịch.

Nói chung, bạn nên bỏ giao dịch trước khi đóng kết nối (giao dịch này sẽ hoàn trả giao dịch nếu giao dịch chưa được thực hiện). Mẫu thông thường trông giống như sau:

using(SqlConnection connection = ...) 
{ 
    connection.Open(); 
    using(SqlTransaction transaction = connection.BeginTransaction()) 
    { 
     ... do stuff ... 
     transaction.Commit(); // commit if all is successful 
    } // transaction.Dispose will be called here and will rollback if not committed 
} // connection.Dispose called here 

Vui lòng gửi mã nếu bạn cần trợ giúp thêm.

+0

Tôi thường sử dụng mô hình này. Tuy nhiên, trong trường hợp này tôi đặc biệt không muốn các hoạt động khác được khôi phục. Tôi chỉ muốn đăng nhập vấn đề và tiếp tục. Giao dịch chỉ được sử dụng làm tăng hiệu suất. – aef123

6

Hãy nhớ rằng ứng dụng của bạn không phải là người tham gia duy nhất trong giao dịch - SQL Server cũng tham gia.

Các lỗi mà bạn trích dẫn:

SqlTransaction này đã hoàn thành; nó không còn sử dụng được nữa. tại System.Data.SqlClient.SqlTransaction.ZombieCheck() tại System.Data.SqlClient.SqlTransaction.Commit()

không chỉ ra các giao dịch đã được cam kết, duy nhất mà nó hoàn tất.

Đề xuất đầu tiên của tôi là máy chủ của bạn đã hủy giao dịch vì quá trình này mất quá nhiều thời gian (thời gian tường thành) hoặc quá lớn (quá nhiều thay đổi hoặc quá nhiều khóa).

Đề xuất thứ hai của tôi là kiểm tra xem bạn có đang dọn dẹp các kết nối và giao dịch một cách thích hợp không.Có thể bạn đang gặp vấn đề bởi vì đôi khi bạn đang cạn kiệt một nhóm tài nguyên trước khi mọi thứ được tự động tái chế.

Ví dụ, DbConnection thực hiện IDisposable, vì vậy bạn cần phải chắc chắn rằng bạn làm sạch một cách thích hợp - với một tuyên bố using nếu bạn có thể, hoặc bằng cách gọi Dispose() trực tiếp nếu bạn không thể. 'DbCommand' là tương tự, vì nó cũng thực hiện IDisposable.

1

Vâng, IMO đầu tiên, bạn không nên mong đợi ứng dụng của mình xử lý các lỗi "cứng" như thế này. Ứng dụng của bạn nên hiểu những quy tắc kinh doanh này là gì và giải thích cho chúng. KHÔNG ép buộc cơ sở dữ liệu của bạn trở thành quy tắc nghiệp vụ hoặc cảnh sát quy tắc ràng buộc. Nó chỉ nên là một cảnh sát quy tắc dữ liệu, và lúc đó, hãy duyên dáng khi nói giao diện với RETURN và các phương thức khác. Công cụ lược đồ không nên ép buộc đến mức đó.

Bật để trả lời câu hỏi của bạn, tôi nghi ngờ, không thấy bất kỳ mã nào của bạn, bạn đang cố thực hiện một cam kết khi xảy ra lỗi và bạn chưa biết. Đây là lý do đằng sau tuyên bố đầu tiên của tôi ... cố gắng bẫy lấy lỗi mà cơ sở dữ liệu đưa ra, mà không cần ứng dụng của bạn hiểu và tham gia vào các quy tắc này, bạn đang đau đầu.

Hãy thử điều này. Chèn các hàng sẽ không không thành công với ràng buộc cơ sở dữ liệu hoặc các lỗi khác và xử lý chèn bằng cam kết. Xem chuyện gì xảy ra. Sau đó, trong một số bản ghi, sẽ không thành công, xử lý cam kết và xem bạn có gặp lỗi đáng yêu hay không. Thứ ba, chạy các lỗi một lần nữa, làm một rollback buộc, và xem nếu bạn thành công.

Chỉ một số ý tưởng. Một lần nữa, như một bản tóm tắt, tôi nghĩ rằng nó đã làm với không bẫy một số lỗi từ cơ sở dữ liệu một cách duyên dáng cho những thứ đó là "cứng" lỗi và mong đợi kết thúc trước để đối phó với họ. Một lần nữa, ý kiến ​​chuyên gia của tôi, NOPE, không làm điều đó. Đó là một mớ hỗn độn. Ứng dụng của bạn cần chồng lấp trong kiến ​​thức về các quy tắc ở mặt sau. Những điều đó được đặt ra chỉ để đảm bảo điều này không xảy ra trong quá trình thử nghiệm, và thỉnh thoảng lỗi trên bề mặt như thế này, sau đó đặt ở phía trước nó để xử lý việc tra cứu bị lãng quên đối với một bộ bàn phím ngoài.

Hy vọng điều đó sẽ hữu ích.

+0

Thông thường tôi đồng ý với bạn về ứng dụng xử lý các quy tắc kinh doanh. Tuy nhiên, trong trường hợp này ứng dụng là công cụ chuyển đổi dữ liệu. Người dùng cuối thiết lập tất cả các quy tắc kinh doanh. Trách nhiệm của người dùng là tạo dựng chuyển đổi theo cách DB chấp nhận. Đây là lý do tại sao tôi cần phải đăng nhập và tiếp tục – aef123

+0

Đồng ý là tốt. Câu trả lời của tôi là hỗ trợ lý do tại sao sự cố xảy ra. Ứng dụng đã không tính IMO lỗi không thành công. Thực hiện proc được lưu trữ như một câu lệnh trong SQL Enterprise Manager (bỏ qua ứng dụng) và xem kết quả là gì, thành công hay không. – SnapJag

0

Bạn đã chứng minh rằng dữ liệu của mình tốt hơn mọi nghi ngờ hợp lý.
FWIW, Tôi muốn di chuyển chèn vào SPROC và không sử dụng giao dịch nào cả.
Nếu bạn cần giao diện người dùng phản hồi, hãy sử dụng nhân viên nền để thực hiện cơ sở dữ liệu.
Với tôi, giao dịch dành cho các hoạt động liên quan, không phải là thiết bị tiết kiệm thời gian. Chi phí chèn phải được thanh toán ở đâu đó dọc theo dòng.

Gần đây tôi đã sử dụng trình thu thập kiến ​​thức ANTS trên ứng dụng cơ sở dữ liệu và đã ngạc nhiên khi thấy các ngoại lệ liên tục SQlClient hiển thị trong mã hoạt động vững chắc. Các lỗi nằm sâu trong khuôn khổ khi mở một kết nối. Họ không bao giờ làm cho nó lên bề mặt và không được phát hiện bởi mã khách hàng. Vì vậy, ... Điểm?
Nó không phải là tất cả đá rắn ra khỏi đó, di chuyển công việc nặng ra khỏi U.I. và chấp nhận chi phí. HTH Bob

7

Cảm ơn tất cả các phản hồi. Tôi đã làm việc với một người nào đó từ MSFT trên diễn đàn MSDN để tìm ra những gì đang xảy ra. Nó chỉ ra vấn đề là do một trong những chèn không thành công do một vấn đề chuyển đổi thời gian ngày.

Vấn đề chính là lỗi này xuất hiện nếu đó là lỗi chuyển đổi ngày. Tuy nhiên, nếu đó là một lỗi khác chẳng hạn như một trường quá dài, nó không gây ra vấn đề này. Trong cả hai trường hợp, tôi mong đợi giao dịch vẫn tồn tại để tôi có thể gọi Rollback trên nó.

Tôi có một chương trình mẫu đầy đủ để nhân rộng vấn đề này. Nếu bất cứ ai muốn xem nó hoặc trao đổi với MSFT bạn có thể tìm thấy các chủ đề trên nhóm tin của MSFT trong microsoft.public.dotnet.framework.adonet theo chủ đề lỗi SqlTransaction.ZombieCheck.

+3

Kết quả cuối cùng là SQL Server tự động quay trở lại giao dịch trên những gì nó xem xét các lỗi nghiêm trọng. Lỗi chuyển đổi DateTime được coi là một trong những lỗi nghiêm trọng này. Hiện tại không có cách nào để ngăn chặn SQL Server chấm dứt giao dịch của bạn cho loại lỗi này. – aef123

3

Ngoại lệ này được ném vì giao dịch DB thực tế đã được khôi phục, do đó đối tượng .NET đại diện cho nó ở phía máy khách đã là "zombie".

Giải thích chi tiết hơn là here. This post giải thích cách viết mã rollback giao dịch chính xác cho các trường hợp như vậy.

1

Vấn đề của tôi là tương tự, và hóa ra là tôi đã tự làm việc đó. Tôi đã chạy một loạt các kịch bản trong một dự án VS2008 để tạo ra các thủ tục được lưu trữ. Trong một trong các procs, tôi đã sử dụng một giao dịch. Kịch bản lệnh đã thực hiện việc tạo proc trong một giao dịch, thay vì bao gồm mã giao dịch như là một phần của thủ tục. Vì vậy, khi nó đã kết thúc mà không có một lỗi, nó cam kết giao dịch cho kịch bản. Mã .Net cũng đang sử dụng và sau đó cam kết một giao dịch, và hiệu ứng zombie đến khi nó cố đóng giao dịch đã được đóng trong kịch bản lệnh SQL. Tôi đã xóa giao dịch khỏi tập lệnh SQL, dựa vào giao dịch đã mở và được kiểm tra trong mã .Net và điều này giải quyết được vấn đề.

1

Theo bài đăng này: http://blogs.msdn.com/b/dataaccesstechnologies/archive/2010/08/24/zombie-check-on-transaction-error-this-sqltransaction-has-completed-it-is-no-longer-usable.aspx

Một giải pháp tạm thời có thể thử bắt rollback hoặc cam kết phẫu thuật. Vì vậy, mã này sẽ đủ để ngăn chặn các lỗi được ném:

public static void TryRollback(this System.Data.IDbTransaction t) 
    { 
     try 
     { 
      t.Rollback(); 
     } 
     catch (Exception ex) 
     { 
      // log error in my case 
     } 
    } 

    public static void TryCommit(this System.Data.IDbTransaction t) 
    { 
     try 
     { 
      t.Commit(); 
     } 
     catch (Exception ex) 
     { 
      // log error in my case 
     } 
    } 

Kiểm tra ví dụ này từ trang web MSDN: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqltransaction.aspx

0

Tôi cũng phải đối mặt với cùng một vấn đề.

This SqlTransaction has completed; it is no longer usable 

Sau khi thực hiện một số nghiên cứu, tôi thấy rằng kích thước tệp nhật ký datatabase của tôi là 40GB.
Đó là khối lượng dữ liệu lớn khiến giao dịch sql của tôi không được cam kết.

Vì vậy, giải pháp của tôi là shrink kích thước tệp nhật ký bằng cách xem this reference link.

CREATE PROCEDURE sp_ShrinkLog_ByDataBaseName(
@DataBaseName VARCHAR(200) 
) 
AS 
BEGIN 

DECLARE @DBName VARCHAR(200) 
DECLARE @LogFileName VARCHAR(200) 
SET @DBName = @DataBaseName 

SELECT @LogFileName = [Logical File Name] 
FROM 
(
SELECT 
    DB_NAME(database_id) AS "Database Name", 
type_desc    AS "File Type", 
name     AS "Logical File Name", 
physical_name   AS "Physical File", 
state_desc   AS "State" 
FROM 
    SYS.MASTER_FILES 
WHERE 
    database_id = DB_ID(@DBName) 
    and type_desc = 'LOG' 
) AS SystemInfo 
SELECT LogFileName = @LogFileName 

EXECUTE(
'USE ' + @DBName + '; 

-- Truncate the log by changing the database recovery model to SIMPLE. 
ALTER DATABASE ' + @DBName + ' 
SET RECOVERY SIMPLE; 

-- Shrink the truncated log file to 1 MB. 
DBCC SHRINKFILE (' + @LogFileName + ', 1); 

-- Reset the database recovery model. 
ALTER DATABASE ' + @DBName + ' 
SET RECOVERY FULL; 

') 
END 

Sau đó, thực hiện EXEC sp_ShrinkLog_ByDataBaseName 'Yor_DB_NAME'.

Nếu cách này không hiệu quả với bạn, đây là another solution mà tôi cho rằng nó sẽ hoạt động.

Các vấn đề liên quan