Yêu cầu không sử dụng giao dịch và sử dụng cách giải quyết để mô phỏng một


43

Tôi đã phát triển T-SQL được vài năm và luôn đào sâu hơn nữa, tiếp tục tìm hiểu tất cả những gì tôi có thể về tất cả các khía cạnh của ngôn ngữ. Gần đây tôi đã bắt đầu làm việc tại một công ty mới và đã nhận được những gì tôi nghĩ là một gợi ý kỳ lạ liên quan đến các giao dịch. Đừng bao giờ sử dụng chúng. Thay vào đó, sử dụng một cách giải quyết mô phỏng một giao dịch. Điều này đến từ DBA của chúng tôi, người làm việc trong một cơ sở dữ liệu với rất nhiều giao dịch và sau đó, rất nhiều chặn. Cơ sở dữ liệu tôi chủ yếu làm việc không gặp phải vấn đề này và tôi thấy các giao dịch đã được sử dụng trong quá khứ.

Tôi hiểu rằng việc chặn được mong đợi với các giao dịch vì bản chất của chúng là làm như vậy và nếu bạn có thể thoát khỏi mà không cần sử dụng, thì bằng mọi cách hãy làm điều đó. Nhưng tôi có nhiều dịp mà mỗi câu lệnh PHẢI thực thi thành công. Nếu một thất bại tất cả họ phải thất bại để cam kết.

Tôi luôn giữ phạm vi giao dịch của mình càng hẹp càng tốt, luôn được sử dụng cùng với SET XACT_ABORT ON và luôn trong TRY / CATCH.

Thí dụ:

CREATE SCHEMA someschema;
GO


CREATE TABLE someschema.tableA
(id   INT NOT NULL IDENTITY(1, 1) PRIMARY KEY, 
 ColA VARCHAR(10) NOT NULL
);
GO

CREATE TABLE someschema.tableB
(id   INT NOT NULL IDENTITY(1, 1) PRIMARY KEY, 
 ColB VARCHAR(10) NOT NULL
); 
GO


CREATE PROCEDURE someschema.ProcedureName @ColA VARCHAR(10), 
                                          @ColB VARCHAR(10)
AS
SET NOCOUNT, XACT_ABORT ON;
BEGIN
BEGIN TRY
    BEGIN TRANSACTION;

    INSERT INTO someschema.tableA(ColA)
    VALUES(@ColA);

    INSERT INTO someschema.tableB(ColB)
    VALUES(@ColB);

--Implement error
    SELECT 1/0 

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF @@trancount > 0
    BEGIN
        ROLLBACK TRANSACTION;
    END;
    THROW;
    RETURN;
END CATCH;
END;
GO

Đây là những gì họ đề nghị mà tôi làm.

GO



CREATE PROCEDURE someschema.ProcedureNameNoTransaction @ColA VARCHAR(10), 
                                                       @ColB VARCHAR(10)
AS
SET NOCOUNT ON;
BEGIN
BEGIN TRY
    DECLARE @tableAid INT;
    DECLARE @tableBid INT;

    INSERT INTO someschema.tableA(ColA)
    VALUES(@ColA);
    SET @tableAid = SCOPE_IDENTITY();

    INSERT INTO someschema.tableB(ColB)
    VALUES(@ColB);
    SET @tableBid = SCOPE_IDENTITY();

--Implement error
    SELECT 1/0 

END TRY
BEGIN CATCH
    DELETE FROM someschema.tableA
    WHERE id = @tableAid;

    DELETE FROM someschema.tableB
    WHERE id = @tableBid;

    THROW;

    RETURN;
END CATCH;
END;
GO

Câu hỏi của tôi cho cộng đồng như sau. Điều này có ý nghĩa như một cách giải quyết khả thi cho các giao dịch?

Ý kiến ​​của tôi từ những gì tôi biết về giao dịch và giải pháp đang đề xuất là không, đây không phải là giải pháp khả thi và đưa ra nhiều điểm thất bại.

Trong cách giải quyết được đề xuất, tôi thấy bốn giao dịch ngầm xảy ra. Hai lần chèn trong thử và sau đó thêm hai giao dịch cho các lần xóa trong lần bắt. Nó thực hiện các thao tác chèn nhưng không quay trở lại nên không có gì thực sự quay trở lại.

Đây là một ví dụ rất cơ bản để chứng minh khái niệm mà họ đang đề xuất. Một số quy trình được lưu trữ thực tế mà tôi đã thực hiện để làm cho chúng hoàn toàn dài và khó quản lý bởi vì việc cuộn lại nhiều tập kết quả với hai giá trị tham số trong ví dụ này trở nên khá phức tạp như bạn có thể tưởng tượng. Vì "quay trở lại" hiện đang được thực hiện thủ công, cơ hội để bỏ lỡ điều gì đó thực sự.

Một vấn đề khác mà tôi nghĩ tồn tại là thời gian chờ hoặc kết nối bị đứt. Điều này vẫn còn được khôi phục? Đây là hiểu biết của tôi về lý do tại sao nên sử dụng SET XACT_ABORT ON để trong những trường hợp này, giao dịch sẽ quay trở lại.

Cảm ơn phản hồi của bạn trước!


4
Nhận xét không phục vụ mục đích đã nêu của họ đã bị xóa hoặc chuyển sang câu trả lời của Wiki cộng đồng.
Paul White

Câu trả lời:


61

Bạn không thể sử dụng các giao dịch trong SQL Server (và có thể là bất kỳ RDBMS thích hợp nào khác). Trong trường hợp không có ranh giới giao dịch rõ ràng ( begin transaction... commit), mỗi câu lệnh SQL sẽ bắt đầu một giao dịch mới, được cam kết ngầm (hoặc được khôi phục) sau khi câu lệnh hoàn thành (hoặc không thành công).

Mô phỏng giao dịch được đề xuất bởi người tự giới thiệu là "DBA" của bạn không đảm bảo ba trong bốn thuộc tính cần thiết của xử lý giao dịch, vì nó chỉ xử lý các lỗi "mềm" và không có khả năng xử lý các lỗi "cứng", chẳng hạn như ngắt kết nối mạng, mất điện, hỏng đĩa, v.v.

  • Nguyên tử: thất bại. Nếu một lỗi "cứng" xảy ra ở đâu đó ở giữa giao dịch giả của bạn, thay đổi sẽ không phải là nguyên tử.

  • Tính nhất quán: thất bại. Theo như trên, dữ liệu của bạn sẽ ở trạng thái không nhất quán sau một lỗi "cứng".

  • Cô lập: thất bại. Có thể một giao dịch giả đồng thời thay đổi một số dữ liệu được sửa đổi bởi giao dịch giả của bạn trước khi hoàn tất giao dịch giả của bạn.

  • Độ bền: thành công. Những thay đổi bạn thực hiện sẽ bền, máy chủ cơ sở dữ liệu sẽ đảm bảo rằng; đây là điều duy nhất cách tiếp cận của đồng nghiệp của bạn không thể làm hỏng.

Khóa là một phương pháp được sử dụng rộng rãi và thành công theo kinh nghiệm để đảm bảo ACIDity của các giao dịch trong tất cả các loại hoặc RDBMS (trang web này là một ví dụ). Tôi thấy rất khó có khả năng một DBA ngẫu nhiên có thể đưa ra một giải pháp tốt hơn cho vấn đề tương tranh hơn hàng trăm, có thể hàng ngàn nhà khoa học máy tính và kỹ sư đã xây dựng một số hệ thống cơ sở dữ liệu thú vị cuối cùng, 50, cái gì? 60 năm? (Tôi nhận ra điều này có phần ngụy biện như một cuộc tranh luận "kháng cáo lên chính quyền", nhưng tôi sẽ không tuân theo nó.)

Để kết luận, hãy bỏ qua lời khuyên của "DBA" nếu bạn có thể, chiến đấu với nó nếu bạn có tinh thần và quay lại đây với các vấn đề tương tranh cụ thể nếu chúng phát sinh.


14

Có một số lỗi nghiêm trọng đến mức khối CATCH không bao giờ được nhập. Từ tài liệu

Các lỗi có mức độ nghiêm trọng từ 20 trở lên đã dừng xử lý tác vụ Công cụ cơ sở dữ liệu SQL Server cho phiên. Nếu xảy ra lỗi có mức độ nghiêm trọng từ 20 trở lên và kết nối cơ sở dữ liệu không bị gián đoạn, TRY ... CATCH sẽ xử lý lỗi.

Sự chú ý, chẳng hạn như yêu cầu ngắt máy khách hoặc kết nối máy khách bị hỏng.

Khi phiên kết thúc bởi quản trị viên hệ thống bằng cách sử dụng câu lệnh KILL.

...

Biên dịch lỗi, chẳng hạn như lỗi cú pháp, ngăn một lô chạy.

Lỗi xảy ra ... vì độ phân giải tên bị hoãn.

Rất nhiều trong số này dễ sản xuất thông qua SQL động. Hoàn tác các tuyên bố như bạn đã hiển thị sẽ không bảo vệ dữ liệu của bạn khỏi các lỗi đó.


2
Phải - và nếu không có gì khác, máy khách chết trong khi thực thi mã sẽ tạo thành một lỗi "nghiêm trọng đến mức khối CATCH không bao giờ được nhập". Cho dù bạn có tin tưởng phần mềm đến mức nào (không chỉ là mã của riêng bạn, mà là MỌI phần của TẤT CẢ các ngăn xếp phần mềm có liên quan), luôn có khả năng xảy ra lỗi phần cứng (một lần nữa, có khả năng ở bất kỳ đâu trong chuỗi) khiến bạn bị lạnh bất cứ lúc nào . Giữ điều này trong tâm trí là một sự bảo vệ tốt chống lại suy nghĩ tử tế dẫn đến loại "cách giải quyết" này.
dgould

2
Ngoài ra, bạn có thể là một nạn nhân bế tắc. Các khối CATCH của bạn chạy nhưng ném nếu chúng cố ghi vào cơ sở dữ liệu.
Joshua

10

i-one : Cách giải quyết được đề xuất cho bạn khiến cho có thể (ít nhất) vi phạm "A" của ACID . Ví dụ: nếu SP đang được thực hiện bởi máy khách từ xa và ngắt kết nối, thì "cam kết" / "rollback" một phần có thể xảy ra, do máy chủ có thể chấm dứt phiên giữa hai lần chèn / xóa (và hủy thực thi SP trước khi kết thúc) .

Điều này có ý nghĩa như một cách giải quyết khả thi cho các giao dịch?

dan-guzman : Không,CATCHkhối không bao giờ được thực thi trong trường hợp hết thời gian truy vấn vì API máy khách đã hủy lô. Nếu không có giao dịch,SET XACT_ABORT ONkhông thể khôi phục bất cứ điều gì ngoài tuyên bố hiện tại.

tibor-karaszi : Bạn có 4 giao dịch, nghĩa là đăng nhập nhiều hơn vào tệp nhật ký giao dịch. Hãy nhớ rằng mỗi giao dịch yêu cầu ghi đồng bộ các bản ghi nhật ký cho đến thời điểm đó, tức là bạn sẽ có hiệu suất kém hơn từ khía cạnh đó khi sử dụng nhiều giao dịch.

rbarryyoung : Nếu họ đang bị chặn rất nhiều thì họ cần phải sửa thiết kế dữ liệu của họ, hợp lý hóa thứ tự truy cập bảng của họ hoặc sử dụng mức cách ly phù hợp hơn. Họ đang cho rằng vấn đề của họ (và không hiểu nó) sẽ trở thành vấn đề của bạn. Bằng chứng từ hàng triệu cơ sở dữ liệu khác là nó sẽ không.

Ngoài ra, những gì họ đang cố gắng thực hiện thủ công thực sự là một sự đồng thời lạc quan của người nghèo. Thay vào đó, những gì họ nên làm là sử dụng một số đồng thời lạc quan tốt nhất trên thế giới, đã được tích hợp vào SQL Server. Điều này đi đến điểm cô lập ở trên. Trong tất cả khả năng họ cần chuyển từ bất kỳ mức cô lập đồng thời bi quan nào mà họ hiện đang sử dụng sang một trong các mức cô lập đồng thời lạc quan, SNAPSHOThoặc READ_COMMITTED_SNAPSHOT. Chúng thực sự sẽ làm điều tương tự như mã thủ công của chúng, ngoại trừ nó sẽ làm điều đó một cách chính xác.

ross-presser : Nếu bạn có các quy trình chạy rất dài - như có gì đó xảy ra hôm nay và tuần tới thì phải làm gì đó và nếu tuần sau thất bại thì hôm nay phải thất bại - bạn có thể muốn xem xét về sagas . Nói đúng ra đây là bên ngoài cơ sở dữ liệu, vì nó yêu cầu một xe buýt dịch vụ.


5

Mã ý tưởng tồi sẽ chỉ tốn kém hơn để sửa chữa dòng.

Nếu có sự cố chặn sử dụng giao dịch rõ ràng (rollback / commit), hãy trỏ DBA của bạn tới internet để biết một số ý tưởng tuyệt vời để giải quyết các vấn đề.

Đây là một cách để giúp giảm bớt việc chặn: https://www.sqlservercentral.com/articles/USE-indexes-to-reduce-blocking-in-conciverse-transilities

Các chỉ mục giảm số lượng tìm kiếm phải xảy ra trong một bảng / trang để tìm một hàng / bộ hàng. Chúng thường được xem như là một phương pháp để giảm thời gian thực hiện cho các truy vấn CHỌN * và cũng đúng. Chúng không được coi là phù hợp cho các bảng liên quan đến số lượng lớn CẬP NHẬT. Trong thực tế, INDEXES được phát hiện là không thuận lợi trong những trường hợp này khi chúng tăng thời gian thực hiện để hoàn thành các truy vấn CẬP NHẬT.

Nhưng đây không phải là luôn luôn như vậy. Đi sâu một chút vào việc thực thi một câu lệnh CẬP NHẬT, chúng tôi thấy rằng nó cũng liên quan đến việc thực hiện một câu lệnh CHỌN trước tiên. Đây là một tình huống đặc biệt và thường thấy trong đó các truy vấn cập nhật các bộ hàng loại trừ lẫn nhau. INDEXES ở đây có thể dẫn đến sự gia tăng đáng kể hiệu suất của công cụ cơ sở dữ liệu trái với niềm tin phổ biến.


4

Chiến lược giao dịch giả là nguy hiểm vì nó cho phép các vấn đề tương tranh mà giao dịch đặc biệt ngăn chặn. Hãy xem xét rằng trong ví dụ thứ hai, bất kỳ dữ liệu nào cũng có thể được thay đổi giữa các câu lệnh.

Các giao dịch giả mạo xóa không được ĐẢM BẢO để chạy hoặc thành công. Nếu máy chủ cơ sở dữ liệu tắt trong giao dịch giả, một số nhưng không phải tất cả các hiệu ứng sẽ vẫn còn. Họ cũng không được đảm bảo để thành công theo cách nói của một giao dịch rollback.

Chiến lược này có thể hoạt động với các phần chèn, nhưng chắc chắn sẽ không hoạt động với các bản cập nhật hoặc xóa (không có câu lệnh SQL của cỗ máy thời gian).

Nếu giao dịch đồng thời nghiêm ngặt gây ra chặn, có rất nhiều giải pháp, thậm chí những giải pháp làm giảm mức độ bảo vệ ... đây là cách chính xác để giải quyết vấn đề.

DBA của bạn đang cung cấp một giải pháp có thể hoạt động tốt nếu chỉ có một người dùng cơ sở dữ liệu, nhưng hoàn toàn không phù hợp với bất kỳ loại sử dụng nghiêm trọng nào.


4

Đây không phải là một vấn đề lập trình, thay vào đó là một vấn đề liên cá nhân / truyền thông. Nhiều khả năng "DBA" của bạn lo lắng về khóa, không phải giao dịch.

Các câu trả lời khác đã giải thích lý do tại sao bạn phải sử dụng các giao dịch ... Ý tôi là RDBMS làm gì, nếu không có giao dịch được sử dụng đúng cách thì không có tính toàn vẹn dữ liệu, vì vậy tôi sẽ tập trung vào cách giải quyết vấn đề thực sự, đó là: tìm hiểu tại sao "DBA" của bạn đã phát triển dị ứng với các giao dịch và thuyết phục anh ấy thay đổi ý định.

Tôi nghĩ rằng anh chàng này đang nhầm lẫn "một kịch bản cụ thể trong đó mã xấu dẫn đến hiệu suất khủng khiếp" với "tất cả các giao dịch đều xấu". Tôi sẽ không mong đợi một DBA có thẩm quyền phạm sai lầm đó, vì vậy điều đó thực sự kỳ lạ. Có lẽ anh ta đã có một trải nghiệm thực sự tồi tệ với một số mã khủng khiếp?

Hãy xem xét một kịch bản như thế này:

BEGIN
UPDATE or DELETE some row, which takes locks it
...do something that takes a while
...perform other queries
COMMIT

Kiểu sử dụng giao dịch này giữ một khóa (hoặc một số khóa), có nghĩa là các giao dịch khác đánh vào cùng một hàng sẽ phải chờ. Nếu các khóa được giữ trong một thời gian dài và đặc biệt là nếu nhiều giao dịch khác muốn khóa cùng một hàng, điều này thực sự có thể làm giảm hiệu suất.

Những gì bạn có thể làm là hỏi anh ta tại sao anh ta có ý tưởng sai lầm tò mò về việc không sử dụng giao dịch, loại truy vấn nào có vấn đề, v.v. Sau đó, hãy thuyết phục anh ta rằng bạn chắc chắn sẽ tránh các tình huống xấu tương tự, rằng bạn sẽ giám sát việc sử dụng khóa của mình và thực hiện, trấn an anh ấy, v.v.

Điều anh ấy nói với bạn là "đừng chạm vào tuốc nơ vít!" Vì vậy, mã bạn đã đăng trong câu hỏi của bạn về cơ bản là sử dụng búa để lái vít. Lựa chọn tốt hơn nhiều là thuyết phục anh ấy biết cách sử dụng tuốc nơ vít ...

Tôi có thể nghĩ ra một vài ví dụ ... tốt, họ đã ở trên MySQL nhưng điều đó cũng hoạt động.

Có một diễn đàn nơi chỉ mục toàn văn mất một lúc để cập nhật. Khi người dùng gửi bài đăng, giao dịch sẽ cập nhật bảng chủ đề để tăng số lượng bài đăng và ngày đăng cuối cùng (do đó khóa hàng chủ đề), sau đó chèn bài đăng và giao dịch sẽ giữ khóa cho đến khi chỉ mục toàn văn bản cập nhật xong và CAM KẾT đã được thực hiện.

Vì điều này chạy trên một rỉ sét với quá ít RAM, nên việc cập nhật chỉ số fulltext cho biết thường dẫn đến vài giây IO ngẫu nhiên dữ dội trên ổ đĩa quay chậm trong hộp.

Vấn đề là những người nhấp vào chủ đề đã gây ra một truy vấn để tăng số lượt xem về chủ đề, điều này cũng yêu cầu khóa trên hàng chủ đề. Do đó, không ai có thể xem chủ đề trong khi chỉ mục toàn văn bản của nó đang cập nhật. Ý tôi là, hàng có thể được đọc, nhưng cập nhật nó sẽ khóa.

Tệ hơn nữa, việc đăng bài sẽ cập nhật số lượng bài đăng trên bảng diễn đàn mẹ và cũng giữ khóa trong khi chỉ mục fulltext đang cập nhật ... làm đóng băng toàn bộ diễn đàn trong vài giây và khiến hàng tấn yêu cầu chồng chất trong hàng đợi máy chủ web .

Giải pháp là lấy các khóa theo đúng thứ tự: BEGIN, chèn bài đăng và cập nhật chỉ mục fulltext mà không lấy bất kỳ khóa nào, sau đó nhanh chóng cập nhật các hàng chủ đề / diễn đàn với số lượng bài đăng và ngày đăng cuối cùng, và CAM KẾT. Điều đó đã giải quyết hoàn toàn vấn đề. Nó chỉ di chuyển xung quanh một vài truy vấn, thực sự đơn giản.

Trong trường hợp này, các giao dịch không phải là vấn đề ... Nó đã có được một khóa không cần thiết trước một hoạt động dài. Các ví dụ khác về những thứ cần tránh trong khi giữ khóa trong giao dịch: chờ người dùng nhập liệu, truy cập nhiều dữ liệu chưa được lưu trữ từ các ổ đĩa quay chậm, IO mạng, v.v.

Tất nhiên, đôi khi, bạn không có lựa chọn nào và bạn phải xử lý lâu trong khi giữ các khóa rườm rà. Có những mánh khóe xung quanh vấn đề này (hoạt động trên một bản sao của dữ liệu, v.v.) nhưng thường thì nút cổ chai hiệu năng xuất phát từ một khóa không được cố ý thu được và chỉ đơn giản là sắp xếp lại các truy vấn sẽ giải quyết vấn đề. Thậm chí tốt hơn, là nhận thức được các khóa được thực hiện trong khi viết các truy vấn ...

Tôi sẽ không lặp lại các câu trả lời khác nhưng thực sự ... sử dụng giao dịch. Vấn đề của bạn là thuyết phục "DBA" của bạn, không hoạt động xung quanh tính năng quan trọng nhất của cơ sở dữ liệu ...


3

TLDR: Sử dụng mức cách ly thích hợp .

Như bạn đã nhận thấy chính xác cách tiếp cận mà không có giao dịch và với phục hồi "thủ công" có thể rất phức tạp. Độ phức tạp cao có nghĩa là thông thường sẽ mất nhiều thời gian hơn để thực hiện nó và mất nhiều thời gian hơn để sửa lỗi (vì độ phức tạp dẫn đến nhiều lỗi hơn trong quá trình thực hiện). Nó có nghĩa là cách tiếp cận như vậy có thể chi phí khách hàng của bạn nhiều hơn nữa.

Mối quan tâm chính của đồng nghiệp "dba" của bạn là hiệu suất. Một trong những cách để cải thiện nó là sử dụng mức cách ly thích hợp. Giả sử bạn có một quy trình cung cấp một số loại dữ liệu tổng quan cho người dùng. Thủ tục như vậy không nhất thiết phải sử dụng mức cô lập SERIALIZABLE. Trong nhiều trường hợp, READ UNCOMMITTED có thể khá đầy đủ. Điều đó có nghĩa là, quy trình đó sẽ không bị chặn bởi giao dịch của bạn tạo hoặc sửa đổi một số dữ liệu.

Tôi khuyên bạn nên xem lại tất cả các chức năng / quy trình hiện có trong cơ sở dữ liệu của mình, đánh giá mức độ cách ly hợp lý cho từng loại, giải thích các lợi ích hiệu suất cho khách hàng của bạn. Sau đó điều chỉnh các chức năng / thủ tục cho phù hợp.


2

Bạn cũng có thể quyết định sử dụng các bảng OLTP trong bộ nhớ. Họ tất nhiên vẫn sử dụng các giao dịch, nhưng không có chặn liên quan.
Thay vì chặn tất cả các hoạt động sẽ thành công, nhưng trong giai đoạn cam kết, công cụ sẽ kiểm tra xung đột giao dịch và một trong những cam kết có thể thất bại. Microsoft sử dụng thuật ngữ "Khóa lạc quan".
Nếu sự cố mở rộng là do xung đột giữa hai thao tác ghi, chẳng hạn như hai giao dịch đồng thời cố gắng cập nhật cùng một hàng, OLTP trong bộ nhớ cho phép một giao dịch thành công và thất bại giao dịch khác. Giao dịch thất bại phải được gửi lại một cách rõ ràng hoặc ngầm định, thử lại giao dịch.
Xem thêm tại: Trong bộ nhớ OLTP


-5

Có một cách xung quanh việc sử dụng các giao dịch ở một mức độ hạn chế và đó là bằng cách thay đổi mô hình dữ liệu của bạn để hướng đối tượng hơn. Vì vậy, thay vì lưu trữ dữ liệu nhân khẩu học ví dụ về một người trong một số bảng và liên quan chúng với nhau và yêu cầu giao dịch, bạn có thể có một tài liệu JSON duy nhất lưu trữ mọi thứ bạn biết về người đó trong một trường. Tất nhiên, làm việc xa đến mức kéo dài tên miền là một thách thức thiết kế khác, được thực hiện tốt nhất bởi các nhà phát triển không phải DBA

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.