Share via


ADO.NET의 데이터 동시성 소개

업데이트: 2007년 11월

여러 사용자가 동시에 데이터를 수정하는 경우 특정 사용자의 수정으로 인해 다른 사용자들이 수정한 내용이 손상되는 것을 막을 수 있도록 컨트롤을 설정해야 합니다. 이러한 상황을 처리하는 시스템을 동시성 제어라고 합니다.

동시성 제어의 유형

일반적으로 데이터베이스에서 동시성을 관리하는 세 가지 방법은 다음과 같습니다.

  • 비관적 동시성 제어: 레코드가 페치되었을 때부터 데이터베이스에서 업데이트될 때까지 해당 행을 다른 사용자가 사용할 수 없습니다.

  • 낙관적 동시성 제어: 실제로 데이터가 업데이트되는 동안만 해당 행을 다른 사용자가 사용할 수 없습니다. 업데이트할 때 데이터베이스의 행을 검사하여 변경된 내용이 있는지 확인합니다. 이미 변경된 레코드를 업데이트하려고 하면 동시성 위반이 발생합니다.

  • "최신으로 업데이트": 실제로 데이터가 업데이트되는 동안만 해당 행을 다른 사용자가 사용할 수 없습니다. 하지만 원본 레코드의 업데이트 내용은 비교하지 않습니다. 레코드에 기록만 하므로 특정 사용자가 레코드를 마지막으로 새로 고친 다음에 다른 사용자가 변경한 내용이 있으면 덮어쓸 수 있습니다.

비관적 동시성

비관적 동시성은 일반적으로 두 가지 경우에 사용됩니다. 첫째, 동일한 레코드에 경합이 많이 발생하는 경우입니다. 데이터에 잠금을 설정하는 것이 동시성 충돌이 발생할 때 변경 내용을 롤백하는 것보다 비용이 적게 듭니다.

비관적 동시성은 트랜잭션 중 레코드를 변경하지 않아야 하는 경우에도 유용합니다. 인벤터리 응용 프로그램이 좋은 예가 될 수 있습니다. 회사 직원이 잠재 고객에 대한 인벤터리를 확인하는 경우를 가정해 보십시오. 대부분은 주문이 생성되어 항목에 주문된 상태 플래그를 표시한 다음 사용 가능한 인벤터리에서 해당 항목을 제거할 때까지 레코드에 잠금을 설정하려고 합니다. 주문이 생성되지 않으면 다른 사용자가 사용 가능한 인벤터리의 정확한 개수를 확인할 수 있도록 잠금이 해제됩니다.

그러나 비관적 동시성 제어는 연결이 끊어진 아키텍처에서는 사용할 수 없습니다. 데이터를 읽거나 업데이트할 수 있을 동안만 연결을 유지하므로 오랜 기간동안 잠금을 지속할 수 없습니다. 그리고 오랫동안 잠금 상태로 있는 응용 프로그램은 확장이 불가능합니다.

낙관적 동시성

낙관적 동시성에서는 데이터베이스에 액세스하는 동안만 잠금이 설정되어 유지됩니다. 잠금을 통해 다른 사용자가 동시에 레코드를 업데이트하는 것을 막을 수 있습니다. 업데이트가 발생하는 순간만 제외하면 항상 데이터를 사용할 수 있습니다. 자세한 내용은 낙관적 동시성(ADO.NET)을 참조하십시오.

업데이트를 시도하면 변경된 행의 원래 버전을 데이터베이스의 기존 행과 비교합니다. 두 행이 서로 다르면 동시성 오류로 인해 업데이트가 되지 않습니다. 따라서 사용자가 기존 비즈니스 논리에 따라 두 행을 조정해야 합니다.

최신으로 업데이트

"최신으로 업데이트"를 사용하면 원본 데이터를 확인하지 않고 업데이트된 내용을 데이터베이스에 기록합니다. 다음과 같은 시나리오가 발생할 수 있습니다.

  • 사용자 A가 데이터베이스의 한 레코드를 페치합니다.

  • 사용자 B가 데이터베이스의 동일한 레코드를 페치하여 수정한 다음 업데이트된 레코드를 다시 데이터베이스에 기록합니다.

  • 사용자 A가 '이전' 레코드를 수정하여 다시 데이터베이스에 기록합니다.

위의 시나리오에서는 사용자 B가 변경한 내용을 사용자 A가 볼 수 없으므로, 동시성 제어의 "최신으로 업데이트" 방법을 사용하려면 이러한 상황이 허용되는지 확인하십시오.

ADO.NET과 Visual Studio의 동시성 제어

데이터 아키텍처는 연결이 끊어진 데이터를 기반으로 하므로 ADO.NET과 Visual Studio에서는 낙관적 동시성을 사용합니다. 따라서 낙관적 동시성으로 인해 발생하는 문제를 해결할 수 있는 비즈니스 논리를 추가해야 합니다.

낙관적 동시성을 사용하는 경우 실제 버전 번호 또는 날짜/시간 스탬프를 사용하는 버전 번호 방식과 모든 값을 저장하는 방식 중 하나를 사용하여 변경 여부를 확인할 수 있습니다.

버전 번호 방식

버전 번호 방식의 경우 업데이트할 레코드에는 날짜/시간 스탬프나 버전 번호를 표시할 열이 있어야 합니다. 레코드를 읽으면 날짜/시간 스탬프나 버전 번호가 클라이언트에 저장됩니다. 이 값도 업데이트할 내용의 일부가 됩니다.

동시성을 처리하는 한 가지 방법은 WHERE 절의 값이 레코드 값과 일치할 때만 업데이트하는 것입니다. 이러한 방식을 SQL로 표현하면 다음과 같습니다.

UPDATE Table1 SET Column1 = @newvalue1, Column2 = @newvalue2 
WHERE DateTimeStamp = @origDateTimeStamp

다음과 같이 버전 번호를 사용하여 비교할 수도 있습니다.

UPDATE Table1 SET Column1 = @newvalue1, Column2 = @newvalue2 
WHERE RowVersion = @origRowVersionValue

날짜/시간 스탬프나 버전 번호가 일치하면 데이터 저장소의 레코드가 변경되지 않았으며 데이터 집합의 새 값으로 안전하게 업데이트할 수 있습니다. 날짜 및 시간 스탬프와 버전 번호가 일치하지 않으면 오류가 반환됩니다. Visual Studio에서 이러한 형식의 동시성 검사를 구현하는 코드를 작성할 수 있습니다. 업데이트 충돌에 응답하는 코드도 작성해야 합니다. 날짜/시간 스탬프나 버전 번호를 정확하게 유지하려면 행의 내용이 변경될 때 함께 업데이트하도록 트리거를 테이블에 설정해야 합니다.

모든 값 저장 방식

날짜/시간 스탬프나 버전 번호를 사용하는 방식에 대한 대안으로 레코드를 읽을 때 모든 필드의 복사본을 만들 수 있습니다. ADO.NET의 DataSet 개체는 수정된 각 레코드에 대해 두 가지 버전을 유지합니다. 즉, 데이터 소스에서 처음에 읽었던 원래 버전과 사용자 업데이트를 나타내는 수정된 버전을 유지합니다. 해당 레코드를 데이터 소스에 다시 기록하려고 하면 데이터 행의 원래 값을 데이터 소스의 레코드와 비교합니다. 두 값이 일치하면 데이터베이스 레코드를 읽은 이후에 값이 변경되지 않은 것입니다. 이런 경우 데이터 집합의 변경된 값이 성공적으로 데이터베이스에 기록됩니다.

각 데이터 어댑터 명령은 네 개의 DELETE, INSERT, SELECT, UPDATE 명령 각각에 대한 매개 변수 컬렉션을 가지고 있습니다. 각 명령에는 수정된 현재 값 그리고 원래 값 모두에 대한 매개 변수가 있습니다.

참고:

새 레코드를 추가하는 INSERT 명령의 경우 원래 레코드가 없으므로 현재 값만 있으면 되고, 레코드를 제거하는 DELETE 명령에서는 삭제할 레코드를 찾기 위한 원래 값만 있으면 됩니다.

다음 예제에서는 일반적인 Customers 테이블을 업데이트하는 데이터 집합 명령의 명령 텍스트를 보여 줍니다. 동적 SQL과 낙관적 동시성에 대한 명령이 지정되어 있습니다.

UPDATE Customers SET CustomerID = @currCustomerID, CompanyName = @currCompanyName, ContactName = @currContactName,
       ContactTitle = currContactTitle, Address = @currAddress, City = @currCity, 
       PostalCode = @currPostalCode, Phone = @currPhone, Fax = @currFax
WHERE (CustomerID = @origCustomerID) AND (Address = @origAddress OR @origAddress IS NULL AND Address IS NULL) AND (City = @origCity OR @origCity IS NULL AND City IS NULL)
      AND (CompanyName = @origCompanyName OR @origCompanyName IS NULL AND CompanyName IS NULL) AND (ContactName = @origContactName OR @origContactName IS NULL AND ContactName IS NULL) AND (ContactTitle = @origContactTitle OR @origContactTitle IS NULL AND ContactTitle IS NULL) 
      AND (Fax = @origFax OR @origFax IS NULL AND Fax IS NULL) AND (Phone = @origPhone OR @origPhone IS NULL AND Phone IS NULL) AND (PostalCode = @origPostalCode OR @origPostalCode IS NULL AND PostalCode IS NULL);
SELECT CustomerID, CompanyName, ContactName, ContactTitle, Address, City,
       PostalCode, Phone, Fax
FROM Customers WHERE (CustomerID = @currCustomerID)

9개의 SET 문 매개 변수는 데이터베이스에 기록될 현재 값을 나타내는 반면 9개의 WHERE 문 매개 변수는 원래 레코드를 찾는 데 사용하는 원래 값을 나타냅니다.

SET 문의 처음 9개 매개 변수는 매개 변수 컬렉션의 처음 9개 매개 변수와 일치합니다. 이러한 매개 변수의 SourceVersion 속성은 Current로 설정되어 있습니다.

WHERE 문에서 그 다음에 오는 9개의 매개 변수는 낙관적 동시성에 사용됩니다. 이러한 자리 표시자는 매개 변수 컬렉션에서 그 다음에 오는 9개의 매개 변수와 일치하며 각 매개 변수의 SourceVersion 속성은 Original로 설정되어 있습니다.

SELECT 문은 업데이트가 발생한 다음 데이터 집합을 새로 고칠 때 사용하며, 고급 SQL 생성 옵션 대화 상자에서 데이터 집합 새로 고침 옵션을 설정하면 생성됩니다.

참고:

위의 SQL 문에서는 명명된 매개 변수를 사용하는 반면 OleDbDataAdapter 명령에서는 물음표(?)를 매개 변수 자리 표시자로 사용합니다.

기본적으로 데이터 어댑터 구성 마법사에서 낙관적 동시성 옵션을 선택하면 Visual Studio에서 이러한 매개 변수를 자동으로 만듭니다. 그러나 비즈니스 요구 사항에 따라 오류를 처리하는 코드를 추가하는 것은 사용자의 책임입니다. ADO.NET에서는 동시성 규칙을 위반하는 행을 반환하는 DBConcurrencyException 개체를 제공합니다. 자세한 내용은 방법: 동시성 오류 처리를 참조하십시오.

참고 항목

작업

방법: 동시성 오류 처리

개념

낙관적 동시성(ADO.NET)

참조

DBConcurrencyException