읽기 트랜잭션을 커밋하거나 롤백해야합니까?


95

격리 수준을 지정할 수 있도록 트랜잭션 내에서 실행하는 읽기 쿼리가 있습니다. 쿼리가 완료되면 어떻게해야합니까?

  • 트랜잭션 커밋
  • 트랜잭션 롤백
  • 아무 작업도하지 않음 (사용 블록이 끝날 때 트랜잭션이 롤백 됨)

각각의 의미는 무엇입니까?

using (IDbConnection connection = ConnectionFactory.CreateConnection())
{
    using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
    {
        using (IDbCommand command = connection.CreateCommand())
        {
            command.Transaction = transaction;
            command.CommandText = "SELECT * FROM SomeTable";
            using (IDataReader reader = command.ExecuteReader())
            {
                // Read the results
            }
        }

        // To commit, or not to commit?
    }
}

편집 : 문제는 트랜잭션을 사용해야하는지 또는 트랜잭션 수준을 설정하는 다른 방법이 있는지 여부가 아닙니다. 문제는 아무것도 수정하지 않는 트랜잭션이 커밋되거나 롤백된다는 차이가 있는지 여부입니다. 성능 차이가 있습니까? 다른 연결에 영향을 줍니까? 다른 차이점이 있습니까?


1
이미 알고있을 것입니다. 그러나 제공 한 예제가 주어지면 다음 쿼리를 간단히 실행하여 동등한 결과를 얻을 수 있습니다. SELECT * FROM SomeTable with NOLOCK
JasonTrue

@Stefan, 우리 대부분은 왜 당신이 읽기 전용 작업으로 거래하는 것을 귀찮게하는지 궁금해하는 것 같습니다. 당신이 NOLOCK에 대해 알고 있다면, 당신이 그 경로를 가지지 않은 이유를 알려주시겠습니까?
StingyJack

NOLOCK에 대해 알고 있지만이 시스템은 SQL Server뿐 아니라 다른 데이터베이스에서도 작동하므로 SQL Server 특정 잠금 힌트를 피하려고합니다. 응용 프로그램이 위의 코드로 잘 작동하기 때문에 이것은 다른 무엇보다 호기심에서 더 많은 질문입니다.
Stefan Moser

아,이 경우 MSSqlServer를 대상 제품으로 표시하므로 sqlserver 태그를 제거합니다.
StingyJack

@StingyJack-맞아요, sqlserver 태그를 사용하지 말았어야 했어요.
Stefan Moser

답변:


51

당신은 커밋합니다. 기간. 다른 현명한 대안은 없습니다. 트랜잭션을 시작한 경우 닫아야합니다. 커밋은 보유하고있을 수있는 모든 잠금을 해제하며 ReadUncommitted 또는 Serializable 격리 수준에서도 똑같이 합리적입니다. 암시 적 롤백에 의존하는 것은 (아마도 기술적으로 동등 할 수 있지만) 형식이 좋지 않습니다.

그것이 당신을 확신하지 못한다면, 당신의 코드 중간에 업데이트 문을 삽입하고 발생하고 그의 데이터를 제거하는 암시 적 롤백을 추적해야하는 다음 사람을 상상해보십시오.


44
합리적인 대안이 있습니다-롤백. 즉, 명시 적 롤백입니다. 변경하려는 의도가 아니라면 롤백을 통해 모든 작업이 취소됩니다. 물론 변경이 없어야합니다. 롤백은이를 보장합니다.
Jonathan Leffler

2
DBMS마다 '암시 적 트랜잭션 완료'의미 체계가 다를 수 있습니다. IBM Informix (그리고 DB2)는 암시 적 롤백을 수행합니다. 소문에 따르면 Oracle은 암시 적 커밋을 수행합니다. 암시 적 롤백을 선호합니다.
Jonathan Leffler

8
임시 테이블을 만들고 ID로 채우고 데이터 테이블과 조인하여 ID와 함께 사용할 데이터를 선택한 다음 임시 테이블을 삭제한다고 가정합니다. 나는 정말로 데이터를 읽는 중이고 임시 테이블이 어떻게되는지 상관하지 않습니다. 임시 테이블이기 때문입니다 ...하지만 성능 측면에서 트랜잭션을 롤백하거나 커밋하는 것이 더 비쌀까요? 임시 테이블과 읽기 작업 만 관련 될 때 커밋 / 롤백의 효과는 무엇입니까?
Triynko

4
@Triynko-직관적으로 ROLLBACK이 더 비싸다고 생각합니다. COMMIT는 일반적인 사용 사례이고 ROLLBACK은 예외적 사례입니다. 하지만 학문적으로 만 제외하고 누가 신경 쓰나요? 앱에 더 나은 최적화 포인트가 1000 개 있다고 확신합니다. 정말 궁금 하다면 bazaar.launchpad.net/~mysql/mysql-server/mysql-6.0/annotate/…
Mark Brackett

3
@Triynko- 최적화 하는 유일한 방법은 프로파일 링입니다. 매우 간단한 코드 변경이므로 실제로 최적화하려는 경우 두 방법을 모두 프로파일 링하지 않을 이유가 없습니다. 결과로 우리를 업데이트하십시오!
Mark Brackett

28

아무것도 변경하지 않은 경우 COMMIT 또는 ROLLBACK을 사용할 수 있습니다. 어느 쪽이든 획득 한 모든 읽기 잠금을 해제하고 다른 변경 사항을 적용하지 않았으므로 동일합니다.


2
동등하다고 알려 주셔서 감사합니다. 제 생각에는 이것이 실제 질문에 가장 잘 대답합니다.
chowey

실제 업데이트없이 커밋을 사용하면 트랜잭션이 비활성화됩니다. 난 그냥 내 라이브 사이트에서 직면했습니다
Muhammad Omer Aslam

6

트랜잭션을 시작하면 항상 커밋하는 것이 가장 좋습니다. use (transaction) 블록 내에서 예외가 발생하면 트랜잭션이 자동으로 롤백됩니다.


3

IMHO 트랜잭션에서 읽기 전용 쿼리를 래핑하는 것이 합리적 일 수 있습니다 (특히 Java에서) 트랜잭션을 "읽기 전용"으로 지정할 수 있으므로 JDBC 드라이버가 쿼리 최적화를 고려할 수 있습니다 (그럴 필요는 없으므로 아무도 INSERT그럼에도 불구하고 발급하는 것을 막을 것입니다 ). 예를 들어, Oracle 드라이버는 읽기 전용으로 표시된 트랜잭션에서 쿼리에 대한 테이블 잠금을 완전히 방지하여 읽기 중심의 애플리케이션에서 많은 성능을 얻습니다.


3

중첩 된 트랜잭션을 고려하십시오 .

대부분의 RDBMS는 중첩 트랜잭션을 지원하지 않거나 매우 제한된 방식으로 에뮬레이션을 시도합니다.

예를 들어 MS SQL Server에서 내부 트랜잭션 (실제 트랜잭션이 아닌 MS SQL Server는 트랜잭션 수준 만 계산합니다!)의 롤백은 가장 바깥 쪽 트랜잭션 (실제 트랜잭션) 에서 발생한 모든 것을 롤백합니다 .

일부 데이터베이스 래퍼는 내부 트랜잭션의 롤백을 오류가 발생했다는 신호로 간주하고 가장 바깥 쪽 트랜잭션이 커밋되거나 롤백되었는지 여부에 관계없이 가장 바깥 쪽 트랜잭션의 모든 항목을 롤백 할 수 있습니다.

따라서 COMMIT는 구성 요소가 일부 소프트웨어 모듈에서 사용된다는 것을 배제 할 수없는 안전한 방법입니다.

이것은 질문에 대한 일반적인 대답입니다. 코드 예제는 새 데이터베이스 연결을 열어 외부 트랜잭션 문제를 영리하게 해결합니다.

성능 관련 : 격리 수준에 따라 SELECT에 다양한 수준의 LOCK 및 임시 데이터 (스냅 샷)가 필요할 수 있습니다. 트랜잭션이 닫히면 정리됩니다. 이것이 COMMIT 또는 ROLLBACK을 통해 수행되는지 여부는 중요하지 않습니다. 사용 된 CPU 시간에는 미미한 차이가있을 수 있습니다. COMMIT는 ROLLBACK (두 문자 미만) 및 기타 사소한 차이보다 구문 분석이 더 빠릅니다. 분명히 이것은 읽기 전용 작업에만 해당됩니다!

완전히 요구되지 않음 : 코드를 읽을 수있는 다른 프로그래머는 ROLLBACK이 오류 조건을 의미한다고 가정 할 수 있습니다.


2

참고로 다음과 같이 코드를 작성할 수도 있습니다.

using (IDbConnection connection = ConnectionFactory.CreateConnection())
using (IDbTransaction transaction = connection.BeginTransaction(IsolationLevel.ReadUncommitted))
using (IDbCommand command = connection.CreateCommand())
{
    command.Transaction = transaction;
    command.CommandText = "SELECT * FROM SomeTable";
    using (IDataReader reader = command.ExecuteReader())
    {
        // Do something useful
    }
    // To commit, or not to commit?
}

그리고 구조를 조금만 변경하면 IDataReader에 대한 using 블록도 맨 위로 이동할 수 있습니다.


1

SQL을 저장 프로 시저에 넣고이를 쿼리 위에 추가하는 경우 :

set transaction isolation level read uncommitted

그러면 C # 코드에서 험난한 작업을 수행 할 필요가 없습니다. 저장 프로 시저에서 트랜잭션 격리 수준을 설정해도 해당 연결의 모든 향후 사용에 설정이 적용되지는 않습니다 (연결이 풀링되기 때문에 다른 설정에 대해 걱정해야 할 사항 임). 저장 프로 시저의 끝에서 연결이 초기화 된 상태로 돌아갑니다.


1

ROLLBACK은 대부분 오류 또는 예외적 인 상황에서 사용되며 성공적으로 완료되면 COMMIT를 사용합니다.

중요하지 않은 읽기 전용 트랜잭션의 경우에도 COMMIT (성공) 및 ROLLBACK (실패)을 사용하여 트랜잭션을 닫아야합니다. 사실 일관성과 미래 보장을 위해 중요합니다.

읽기 전용 트랜잭션은 다음과 같이 여러 가지 방식으로 논리적으로 "실패"할 수 있습니다.

  • 쿼리가 예상대로 정확히 하나의 행을 반환하지 않습니다.
  • 저장 프로 시저에서 예외가 발생 함
  • 가져온 데이터가 일치하지 않습니다.
  • 너무 오래 걸리기 때문에 사용자가 트랜잭션을 취소합니다.
  • 교착 상태 또는 시간 초과

읽기 전용 트랜잭션에 COMMIT 및 ROLLBACK을 올바르게 사용하면 캐싱, 감사 또는 통계와 같은 특정 시점에 DB 쓰기 코드가 추가되면 예상대로 계속 작동합니다.

암시 적 ROLLBACK은 응용 프로그램이 충돌하거나 복구 할 수없는 오류, 네트워크 오류, 정전 등으로 종료되는 "심각한 오류"상황에만 사용해야합니다.


0

READ가 상태를 변경하지 않는다는 점을 감안하면 아무것도하지 않습니다. 커밋을 수행하는 것은 데이터베이스에 요청을 보내는주기를 낭비하는 것 외에는 아무것도하지 않습니다. 상태가 변경된 작업을 수행하지 않았습니다. 롤백도 마찬가지입니다.

그러나 개체를 정리하고 데이터베이스에 대한 연결을 닫아야합니다. 연결을 닫지 않으면이 코드가 반복적으로 호출되는 경우 문제가 발생할 수 있습니다.


3
격리 수준에 따라 다른 트랜잭션을 차단하는 잠금을 선택할 수 있습니다.
Graeme Perrow

연결은 사용 블록이 끝날 때 닫히게됩니다. 그러나 네트워크 트래픽은 아마도 방정식에서 가장 느린 부분 일 것입니다.
Joel Coehoorn

1
트랜잭션은 어떤 방식 으로든 커밋되거나 롤백되므로 성공한 경우 항상 커밋을 실행하는 것이 좋습니다.
Neil Barnwell

0

AutoCommit을 false로 설정하면 YES입니다.

JDBC (Postgresql 드라이버)를 사용한 실험에서 선택 쿼리가 중단되면 (시간 초과로 인해) 롤백하지 않는 한 새 선택 쿼리를 시작할 수 없음을 발견했습니다.


-2

코드 샘플에서

  1. // 유용한 작업 수행

    데이터를 변경하는 SQL 문을 실행하고 있습니까?

그렇지 않은 경우 "읽기"트랜잭션과 같은 것은 없습니다. 삽입, 업데이트 및 삭제 문 (데이터를 변경할 수있는 문)의 변경 사항 만 트랜잭션에 있습니다 ... 당신이 말하는 것은 SQL에 대한 잠금입니다. 서버는 해당 데이터에 영향을 미치는 다른 트랜잭션으로 인해 읽고있는 데이터를 저장합니다. 이러한 잠금 수준은 SQL Server 격리 수준에 따라 다릅니다.

그러나 SQL 문이 아무것도 변경하지 않았다면 커밋하거나 롤백 할 수 없습니다.

데이터를 변경하는 경우 명시 적으로 트랜잭션을 시작하지 않고도 격리 수준을 변경할 수 있습니다. 모든 개별 SQL 문은 암시 적으로 트랜잭션에 있습니다. 트랜잭션을 명시 적으로 시작하는 것은 두 개 이상의 문이 동일한 트랜잭션 내에 있는지 확인하는 데만 필요합니다.

트랜잭션 격리 수준을 설정하기 만하면 명령의 CommandText를 "Set Transaction Isolation level Repeatable Read"(또는 원하는 수준)로 설정하고 CommandType을 CommandType.Text로 설정 한 다음 명령을 실행하면됩니다. (Command.ExecuteNonQuery () 사용할 수 있습니다)

참고 : MULTIPLE read 문을 수행하고 있고 이들 모두가 첫 번째 것과 동일한 데이터베이스 상태를 "보도록"하려면 격리 수준 최상위 반복 읽기 또는 직렬화 가능 ...을 설정해야합니다.


// 유용한 일을하면 데이터가 변경되지 않고 읽기만됩니다. 내가 원하는 것은 쿼리의 격리 수준을 지정하는 것뿐입니다.
Stefan Moser

그런 다음 클라이언트에서 명시 적으로 트랜잭션을 시작하지 않고이를 수행 할 수 있습니다. "Set Transaction Isolation Level ReadUncommitted", "... Read Committed", "... RepeatableRead", "... Snapshot"SQL 문자열을 실행하면됩니다. , 또는 "... 직렬화 가능" "커밋 된 격리 수준 읽기 설정"
Charles Bretana

3
읽기만하는 경우에도 거래는 여전히 중요합니다. 여러 읽기 작업을 수행하려는 경우 트랜잭션 내에서 수행하면 일관성이 보장됩니다. 하나없이하는 것은 그렇지 않습니다.
MarkR

네 죄송합니다. 맞습니다. 최소한 Isolation 수준이 Repeatable Read 이상으로 설정된 경우에는 해당됩니다.
Charles Bretana

-3

다른 사람이 동일한 데이터를 읽지 못하도록 차단해야합니까? 왜 거래를 사용합니까?

@Joel-내 질문은 "왜 읽기 쿼리에 트랜잭션을 사용합니까?"로 표현하는 것이 좋습니다.

@Stefan-저장된 proc이 아닌 AdHoc SQL을 사용하려는 경우 쿼리의 테이블 뒤에 WITH (NOLOCK)를 추가하면됩니다. 이렇게하면 트랜잭션에 대한 응용 프로그램과 데이터베이스에서 오버 헤드 (최소한)가 발생하지 않습니다.

SELECT * FROM SomeTable WITH (NOLOCK)

EDIT @ Comment 3 : 질문 태그에 "sqlserver"가 있으므로 MSSQLServer가 대상 제품이라고 가정했습니다. 이제 그 점이 명확 해 졌으므로 태그를 편집하여 특정 제품 참조를 제거했습니다.

나는 왜 당신이 처음에 읽기 작업에서 트랜잭션을 만들고 싶은지 잘 모르겠습니다.


1
한 번에 설정된 격리 수준으로. 트랜잭션을 사용하여 실제로 쿼리에 대한 잠금 양을 줄일 수 있습니다.
Joel Coehoorn

1
더 낮은 격리 수준을 사용하고 잠금을 줄일 수 있도록 트랜잭션을 사용하고 있습니다.
Stefan Moser

@StingyJack-이 코드는 여러 데이터베이스에 대해 실행할 수 있으므로 NOLOCK은 옵션이 아닙니다.
Stefan Moser
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.