트랜잭션을 사용하지 말고 해결 방법을 사용하여 트랜잭션을 시뮬레이트하도록 요청


43

나는 몇 년 동안 T-SQL을 개발해 왔으며 항상 더 깊이 파고 들어 언어의 모든 측면에 대해 가능한 모든 것을 계속 배우고 있습니다. 나는 최근에 새로운 회사에서 일하기 시작했고 거래에 관한 이상한 제안을 받았다. 절대 사용하지 마십시오. 대신 트랜잭션을 시뮬레이트하는 임시 해결책을 사용하십시오. 이것은 많은 트랜잭션과 많은 블로킹으로 하나의 데이터베이스에서 작업하는 DBA에서 온 것입니다. 내가 주로 일하는 데이터베이스는이 문제로 고통받지 않으며 과거에 트랜잭션이 사용 된 것으로 보입니다.

나는 본질적으로 거래가 차단되기 때문에 차단이 예상된다는 것을 알고 있으며, 하나를 사용하지 않고 도망 갈 수 있다면 반드시 그렇게하십시오. 그러나 각 문장이 성공적으로 실행되어야하는 경우가 많이 있습니다. 하나가 실패하면 모두 커밋하지 않아야합니다.

나는 항상 거래 범위를 가능한 한 좁게 유지했으며 항상 SET XACT_ABORT ON과 함께 사용되며 항상 TRY / CATCH 내에서 사용되었습니다.

예:

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

그들이 제안한 것은 다음과 같습니다.

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

커뮤니티에 대한 나의 질문은 다음과 같습니다. 이것이 트랜잭션에 대한 실행 가능한 대안으로 이해가됩니까?

내가 트랜잭션에 대해 알고 솔루션이 제안하는 것에 대한 나의 의견은 아니요, 이것은 가능한 솔루션이 아니며 많은 실패 지점을 초래한다는 것입니다.

제안 된 해결 방법에서 네 개의 암시 적 트랜잭션이 발생합니다. 시도에 두 개의 삽입이 있고 캐치에있는 삭제에 대한 두 개의 추가 트랜잭션이 있습니다. 인서트를 "취소"하지만 롤백하지 않으므로 실제로 롤백되는 것은 없습니다.

이것은 그들이 제안하는 개념을 보여주는 매우 기본적인 예입니다. 이 예제에서 여러 결과 집합과 두 개의 매개 변수 값을 "롤백"하면 상상할 수 있듯이 상당히 복잡해지기 때문에이 작업을 수행 한 실제 저장 프로 시저 중 일부는 철저하게 길고 관리하기가 어렵습니다. "롤백"은 지금 수동으로 수행되기 때문에 실제로 무언가를 놓칠 기회가 있습니다.

내가 생각하는 또 다른 문제는 시간 초과 또는 연결 끊어짐입니다. 여전히 롤백됩니까? 이것이 SET XACT_ABORT ON을 사용해야하는 이유에 대한 이해입니다. 이러한 경우 트랜잭션이 롤백됩니다.

미리 의견을 보내 주셔서 감사합니다.


4
명시된 목적에 맞지 않는 의견은 삭제되었거나 커뮤니티 위키 답변으로 이동되었습니다.
Paul White

답변:


61

당신은 할 수 없습니다 SQL 서버 (그리고 아마도 다른 적절한 RDBMS)에서 트랜잭션을 사용합니다. 명시 적 트랜잭션 경계 ( begin transaction... commit) 가 없으면 각 SQL 문은 새 트랜잭션을 시작합니다.이 트랜잭션은 명령문이 완료되거나 실패한 후에 내재적으로 커미트 (또는 롤백)됩니다.

"DBA"로 제시 한 사람이 제안한 트랜잭션 시뮬레이션은 "소프트"오류 만 처리하고 "하드"오류를 처리 할 수 ​​없기 때문에 트랜잭션 처리의 4 가지 필수 속성 중 3 가지를 보장하지 못합니다. 네트워크 연결 끊기, 정전, 디스크 오류 등이 있습니다.

  • 원 자성 : 실패. 의사 트랜잭션 중간에 "하드"오류가 발생하면 변경 사항은 비원 자적입니다.

  • 일관성 : 실패 위의 내용에 따라 "하드"오류로 인해 데이터가 일관성이없는 상태가됩니다.

  • 격리 : 실패 동시 의사 트랜잭션 은 의사 트랜잭션이 완료되기 전에 의사 트랜잭션에 의해 수정 된 일부 데이터를 변경할 수 있습니다.

  • 내구성 : 성공. 변경 사항 내구성 있으며 데이터베이스 서버는이를 보장합니다. 이것이 동료의 접근 방식으로 해결할 수없는 유일한 것입니다.

잠금은 모든 종류 또는 RDBMS에서 트랜잭션의 ACID를 보장하기 위해 널리 사용되고 경험적으로 성공적인 방법입니다 (이 사이트는 예입니다). 무작위 DBA가 동시성 문제에 대한 더 나은 해결책을 제시 할 가능성은 거의 없습니다. 지난 수백 년 동안 흥미로운 데이터베이스 시스템을 구축해온 수백 명의 컴퓨터 과학자와 엔지니어가 무엇입니까? 60 년? (이것은 "권한에 대한 이의 제기"주장으로 다소 틀렸다는 것을 알고 있지만, 나는 그것을 무시할 것이다.)

결론적으로, 가능하다면 "DBA"의 조언을 무시하고, 정신이 있으면 싸우고, 특정 동시성 문제가 발생하면 다시 여기로 돌아 오십시오.


14

CATCH 블록이 입력되지 않을 정도로 심각한 오류가 있습니다. 로부터 문서

세션의 SQL Server 데이터베이스 엔진 작업 처리를 중지시키는 심각도가 20 이상인 오류. 심각도가 20 이상인 오류가 발생하고 데이터베이스 연결이 중단되지 않으면 TRY ... CATCH가 오류를 처리합니다.

클라이언트 인터럽트 요청 또는 손상된 클라이언트 연결과 같은주의

KILL 문을 사용하여 시스템 관리자가 세션을 종료 한 경우

...

배치 실행을 방해하는 구문 오류와 같은 오류를 컴파일하십시오.

지연된 이름 확인으로 인해 발생하는 오류입니다.

동적 SQL을 통해 많은 것을 쉽게 생성 할 수 있습니다. 표시 한 것과 같은 실행 취소 문은 이러한 오류로부터 데이터를 보호하지 않습니다.


2
맞습니다. 그 밖의 아무것도 없다면 코드를 실행하는 동안 클라이언트가 죽으면 "CATCH 블록이 입력되지 않을 정도로 심각합니다"라는 오류가 발생합니다. 소프트웨어를 신뢰하는 정도 (자신의 코드뿐만 아니라 관련된 모든 소프트웨어 스택의 모든 부분)에 관계없이 항상 하드웨어 고장 (체인의 어느 곳에서나 발생할 수 있음)이 발생할 수 있습니다. . 이것을 염두에 두는 것은 이런 종류의 "해결 방법"으로 이어지는 친절한 사고에 대한 좋은 방어입니다.
dgould

2
또한 교착 상태의 피해자 일 수 있습니다. CATCH 블록은 실행되지만 데이터베이스에 쓰려고하면 throw됩니다.
여호수아

10

i-one : 제안 된 해결 방법으로 ACID 의 "A"를 위반할 수있습니다. 예를 들어, SP가 원격 클라이언트에 의해 실행되고 연결이 끊어지면 서버가두 삽입 / 삭제 사이의 세션을 종료 할 수 있기 때문에 부분 "커밋"/ "롤백"이 발생할 수 있습니다(SP 실행이 종료되기 전에 중단) .

이것이 트랜잭션에 대한 실행 가능한 대안으로 이해가됩니까?

dan-guzman : 아니요CATCH. 클라이언트 API가 일괄 처리를 취소했기 때문에 쿼리 시간이 초과 된 경우 블록이 실행되지 않습니다. 트랜잭션이 없으면SET XACT_ABORT ON현재 명령문 이외의 다른 것을 롤백 할 수 없습니다.

tibor-karaszi : 4 개의 트랜잭션이 있는데, 이는 트랜잭션 로그 파일에 더 많은 로깅을 의미합니다. 각 트랜잭션에는 해당 시점까지 로그 레코드를 동기식으로 작성해야합니다. 즉, 많은 트랜잭션을 사용할 때 해당 측면에서도 성능이 저하됩니다.

rbarryyoung : 차단이 많이 발생하면 데이터 디자인을 수정하거나 테이블 액세스 순서를 합리화하거나보다 적절한 격리 수준을 사용해야합니다. 그들은 그들의 문제 (그리고 그것을 이해하지 못하면)가 당신의 문제가 될 것이라고 가정하고 있습니다. 수백만 개의 다른 데이터베이스의 증거는 그렇지 않다는 것입니다.

또한 수동으로 구현하려는 것은 사실상 가난한 사람들의 낙관적 동시성입니다. 대신에 SQL Server에 이미 내장되어있는 세상에서 가장 낙관적 인 동시성을 사용하는 것이 좋습니다. 위의 격리 지점으로 이동합니다. 십중팔구 그들은 현재 낙관적 동시성 분리 레벨 중 하나를 사용하는 어떤 비관적 동시성 격리 수준에서 전환해야 SNAPSHOT하거나 READ_COMMITTED_SNAPSHOT. 이것들은 올바르게 수행하는 것을 제외하고는 수동 코드와 동일한 작업을 효과적으로 수행합니다.

로스 - 프레 : 당신은 매우 긴 프로세스를 실행 한 경우 - 뭔가 오늘과 다음 주 뭔가가 수행해야하고, 다음 주 일이 실패하면 오늘날의 소급 실패하는 일처럼을 - 당신이 조사 할 수 있습니다 무용담 . 엄밀히 말하면 서비스 버스가 필요하기 때문에 데이터베이스 외부에 있습니다.


5

잘못된 아이디어 코드는 줄을 고치는 데 더 많은 비용이들 것입니다.

명시 적 트랜잭션 (롤백 / 커밋)을 사용하여 차단하는 데 문제가있는 경우 DBA를 인터넷으로 지정하여 문제를 해결할 수있는 훌륭한 아이디어를 찾으십시오.

다음은 차단을 완화하는 데 도움이되는 방법입니다. https://www.sqlservercentral.com/articles/using-indexes-to-reduce-blocking-in-concurrent-transactions

인덱스는 행 / 행 세트를 찾기 위해 테이블 ​​/ 페이지에서 발생해야하는 탐색 수를 줄입니다. 일반적으로 SELECT * 쿼리의 실행 시간을 단축하는 방법으로도 사용됩니다. 많은 수의 UPDATES에 관련된 테이블에는 적합하지 않은 것으로 간주됩니다. 실제로 INDEXES는 UPDATE 쿼리를 완료하는 데 걸리는 시간이 길어 지므로 바람직하지 않은 것으로 밝혀졌습니다.

그러나 항상 그런 것은 아닙니다. UPDATE 문의 실행에 대해 약간 깊이 살펴보면 먼저 SELECT 문을 실행하는 것도 포함됩니다. 이것은 쿼리가 상호 배타적 인 행 집합을 업데이트하는 특별하고 자주 볼 수있는 시나리오입니다. 여기에서 INDEXES는 널리 알려진 신념과 달리 데이터베이스 엔진의 성능을 크게 향상시킬 수 있습니다.


4

가짜 거래 전략은 거래가 구체적으로 방지하는 동시성 문제를 허용하므로 위험합니다. 두 번째 예에서는 모든 데이터가 명령문간에 변경 될 수 있음을 고려하십시오.

가짜 트랜잭션 삭제는 실행되거나 성공하도록 보장되지 않습니다. 가짜 트랜잭션 중에 데이터베이스 서버가 꺼지면 일부 효과는 유지되지 않습니다. 또한 트랜잭션 롤백과 같은 방식으로 성공한다고 보장되지 않습니다.

이 전략은 삽입에서 작동하지만 업데이트 또는 삭제에서는 작동하지 않습니다 (타임머신 SQL 문 없음).

엄격한 트랜잭션 동시성으로 인해 차단이 발생하는 경우 보호 수준을 낮추는 솔루션도 많지만 문제를 해결하는 올바른 방법입니다.

DBA는 데이터베이스 사용자가 한 명인 경우에는 정상적으로 작동하지만 심각한 사용에는 전혀 적합하지 않은 솔루션을 제공합니다.


4

이것은 프로그래밍 문제가 아니라 대인 관계 / 잘못된 의사 소통 문제입니다. 대부분의 "DBA"는 거래가 아니라 잠금에 대해 걱정하고 있습니다.

다른 답변은 이미 트랜잭션을 사용해야하는 이유를 설명합니다 ... RDBMS가 올바르게 사용하는 트랜잭션이 없으면 데이터 무결성이 없으므로 실제 문제를 해결하는 방법에 초점을 맞출 것입니다. 이유를 찾으십시오. 당신의 "DBA"는 거래에 알레르기를 일으켜 그의 마음을 바꾸도록 설득했습니다.

이 사람은 "잘못된 코드로 인해 성능이 좋지 않은 특정 시나리오"와 "모든 트랜잭션이 나쁘다"고 혼동하고 있다고 생각합니다. 유능한 DBA가 그 실수를 할 것으로 기대하지 않으므로 정말 이상합니다. 어쩌면 그는 끔찍한 코드에 대해 정말로 나쁜 경험을 했습니까?

다음과 같은 시나리오를 고려하십시오.

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

이 스타일의 트랜잭션 사용에는 잠금 (또는 여러 잠금)이 있으므로 동일한 행에 도달하는 다른 트랜잭션은 기다려야합니다. 잠금이 오랫동안 유지되는 경우, 특히 많은 다른 트랜잭션이 동일한 행을 잠 그려는 경우 성능이 실제로 저하 될 수 있습니다.

당신이 할 수있는 일은 그가 트랜잭션을 사용하지 않는다는 이상한 생각이있는 이유, 문제가되는 쿼리 유형 등을 묻는 것입니다. 그런 다음 비슷한 나쁜 시나리오를 피하고 잠금 사용을 모니터링 할 것입니다. 성능, 그를 안심시키는 등

그가 말한 것은 "드라이버를 만지지 마십시오!" 질문에 게시 한 코드는 기본적으로 망치를 사용하여 나사를 구동합니다. 훨씬 더 좋은 방법은 스크류 드라이버 사용법을 알고있는 것입니다.

몇 가지 예를 생각할 수 있습니다 ... 음, 그들은 MySQL에 있었지만 작동해야합니다.

전체 텍스트 색인을 업데이트하는 데 시간이 걸리는 포럼이있었습니다. 사용자가 게시물을 제출하면 트랜잭션은 항목 테이블을 업데이트하여 게시물 수와 마지막 게시물 날짜를 늘린 다음 (토픽 행 잠금) 게시물을 삽입하고 전체 텍스트 색인의 업데이트가 완료 될 때까지 트랜잭션이 잠금을 유지합니다. 그리고 커밋이 완료되었습니다.

이것은 RAM이 너무 적은 러스트 버킷에서 실행되었으므로 전체 텍스트 인덱스를 업데이트하면 상자의 단일 느린 회전 드라이브에서 몇 초의 강렬한 임의 IO가 발생하는 경우가 종종있었습니다.

문제는 주제를 클릭 한 사람들이 쿼리로 인해 주제에 대한 조회수가 증가하여 주제 행에 대한 잠금이 필요하다는 것입니다. 따라서 전체 텍스트 색인이 업데이트되는 동안 아무도 주제를 볼 수 없습니다. 행을 읽을 수는 있지만 업데이트하면 잠길 수 있습니다.

더 나쁜 것은 게시하면 상위 포럼 테이블의 게시물 수를 업데이트하고 전체 텍스트 색인이 업데이트되는 동안 잠금을 유지합니다. 이는 전체 포럼을 몇 초 동안 중단시키고 수많은 요청이 웹 서버 큐에 쌓이게합니다. .

해결책은 올바른 순서로 잠금을 수행하는 것입니다. BEGIN, 게시물을 삽입하고 잠금없이 전체 텍스트 색인을 업데이트 한 다음 게시물 수와 마지막 게시물 날짜 및 주제와 함께 주제 / 포럼 행을 신속하게 업데이트하십시오. 그것은 문제를 완전히 해결했습니다. 정말 간단한 쿼리 몇 가지를 둘러 보았습니다.

이 경우 트랜잭션은 문제가되지 않았습니다. 긴 작업 전에 불필요한 잠금을 획득하고있었습니다. 트랜잭션에서 잠금을 유지하는 동안 피해야 할 다른 예 : 사용자 입력 대기, 느리게 회전하는 드라이브에서 많은 캐시되지 않은 데이터에 액세스, 네트워크 IO 등

물론, 때로는 선택의 여지가 없으며 번거로운 잠금을 유지하는 동안 긴 처리를 수행해야합니다. 이 문제를 해결하는 데는 트릭이 있지만 (데이터 사본 등에서 작동) 성능 병목 현상은 의도적으로 얻지 못한 잠금에서 발생하며 쿼리를 다시 정렬하면 문제가 해결됩니다. 더 좋은 것은 쿼리를 작성하는 동안 잠금을 인식하는 것입니다 ...

나는 다른 답변을 반복하지 않지만 실제로는 ... 거래를 사용합니다. 문제는 데이터베이스의 가장 중요한 기능을 해결하지 않고 "DBA"를 설득하는 것입니다 ...


3

TLDR : 적절한 격리 수준을 사용하십시오 .

트랜잭션이없고 "수동"복구 방식을 사용하면 접근 방식이 매우 복잡 할 수 있습니다. 복잡성이 높다는 것은 일반적으로 구현하는 데 훨씬 많은 시간과 오류를 해결하는 데 더 많은 시간을 의미합니다 (복잡성으로 인해 구현에 더 많은 오류가 발생하기 때문). 이는 그러한 접근 방식이 고객에게 훨씬 많은 비용을 초래할 수 있음을 의미합니다.

"dba"동료의 주요 관심사는 성능입니다. 이를 개선하는 방법 중 하나는 적절한 격리 수준을 사용하는 것입니다. 사용자에게 일종의 개요 데이터를 제공하는 절차가 있다고 가정합니다. 이러한 절차는 반드시 직렬화 가능 분리 레벨을 사용할 필요는 없습니다. 많은 경우 READ UNCOMMITTED로 충분할 수 있습니다. 이는 일부 데이터를 생성하거나 수정하는 트랜잭션에 의해 그러한 절차 가 차단되지 않음을 의미 합니다.

데이터베이스의 모든 기존 기능 / 프로 시저를 검토하고 각각의 합리적인 격리 수준을 평가하고 고객에게 성능 이점을 설명하는 것이 좋습니다. 그런 다음이 기능 / 절차를 적절히 조정하십시오.


2

In-Memory OLTP 테이블을 사용하도록 결정할 수도 있습니다. 물론 여전히 트랜잭션을 사용하지만 관련된 차단은 없습니다.
모든 작업을 차단하는 대신 성공하지만 커밋 단계 엔진에서 트랜잭션 충돌을 확인하여 커밋 중 하나가 실패 할 수 있습니다. Microsoft는 "최적 잠금"이라는 용어를 사용합니다.
동일한 행을 업데이트하려고하는 두 개의 동시 트랜잭션과 같은 두 쓰기 작업 간의 충돌로 인해 스케일링 문제가 발생한 경우 In-Memory OLTP는 한 트랜잭션이 성공하고 다른 트랜잭션이 실패합니다. 실패한 트랜잭션은 트랜잭션을 재 시도하면서 명시 적 또는 암시 적으로 다시 제출해야합니다.
추가 정보 : 메모리 OLTP


-5

트랜잭션을 제한적으로 사용하는 방법이 있으며 데이터 모델을 더 객체 지향으로 변경하는 것입니다. 예를 들어 사람에 대한 인구 통계 데이터를 여러 테이블에 저장하고 서로 관련시키고 거래를 요구하는 대신, 해당 사람에 대해 알고있는 모든 것을 단일 필드에 저장하는 단일 JSON 문서를 가질 수 있습니다. 물론 도메인 확장을 해결하는 것은 DBA가 아닌 개발자가 가장 잘 수행하는 또 다른 디자인 과제입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.