분리를 방지하려면 ropeatableRead
-
02-07-2019 - |
문제
PayPal Instant 지불 알림을받을 때 제품 (배송 보험 정책과 같은)을 만들어야하는 응용 프로그램을 연구하고 있습니다. 불행히도 PayPal은 때때로 중복 알림을 보냅니다. 또한 PayPal에서 업데이트를받을 때 웹 서비스 업데이트를 동시에 수행하는 또 다른 타사가 있습니다.
다음은 관련된 데이터베이스 테이블의 기본 다이어그램입니다.
// table "package"
// columns packageID, policyID, other data...
//
// table "insurancepolicy"
// columns policyID, coverageAmount, other data...
다음은 내가하고 싶은 일의 기본 다이어그램입니다.
using (SqlConnection conn = new SqlConnection(...))
{
sqlTransaction sqlTrans = conn.BeginTransaction(IsolationLevel.RepeatableRead);
// Calls a stored procedure that checks if the foreign key in the transaction table has a value.
if (PackageDB.HasInsurancePolicy(packageID, conn))
{
sqlTrans.Commit();
return false;
}
// Insert row in foreign table.
int policyID = InsurancePolicyDB.Insert(coverageAmount, conn);
if (policyID <= 0)
{
sqlTrans.Rollback();
return false;
}
// Assign foreign key to parent table. If this fails, roll back everything.
bool assigned = PackageDB.AssignPolicyID(packageID, policyID, conn);
if (!assigned)
{
sqlTrans.Rollback();
return false;
}
}
이 작업을 동시에 수행하는 두 개의 (또는 그 이상) 스레드 (또는 프로세스 또는 응용 프로그램)가있는 경우 정책이 만들어지고 정책이 할당 될 때까지 첫 번째 스레드가 정책 정보가없는 상태에서 "패키지"행을 잠그는 것을 원합니다. 패키지 테이블에. 그런 다음 PolicyID가 패키지 테이블에 할당 된 후 잠금이 해제됩니다. 이 동일한 코드를 호출하는 다른 스레드가 패키지 행을 읽을 때 정책 ID가 없는지 확인할 때 일시 중지되기를 바랍니다. 첫 번째 트랜잭션의 잠금 장치가 릴리스되면 두 번째 트랜잭션이 정책 ID가 있음을 알기를 바랍니다. 따라서 정책 테이블에 행을 삽입하지 않고 반환합니다.
참고 : CRUD 데이터베이스 설계로 인해 저장된 각 절차에는 읽기 (선택), 작성 (삽입) 또는 업데이트가 포함됩니다.
이것이 반복적 인 트랜잭션 격리의 올바른 사용입니까?
감사.
해결책
더 안전하고 깨끗합니다 insert into Policy
중복을 삽입하려는 시도에서 일부 독창성 테이블 제약 조건을 누르십시오. 격리 수준을 높이면 동시성을 낮추고 교착 상태와 같은 다른 불쾌한 문제로 이어질 수 있습니다.
또 다른 방법은 항상 정책 행을 삽입 한 다음 패키지가 이미 정책에 첨부 된 경우 다시 롤백하는 것입니다.
begin tran (read committed)
/* tentatively insert new Policy */
insert Policy
/* attach Package to Policy if it's still free */
update Package
set Package.policy_id = @policy_id
where Package.package_id = @package_id and Package.policy_id is null
if @@rowcount > 0
commit
else
rollback
이것은 갈등이 드물면 가장 잘 작동합니다. 이는 귀하의 경우 인 것 같습니다.
다른 팁
나는 당신이 실제로 직렬화 가능한 격리 수준을 원한다고 생각합니다. 문제는 두 개의 스레드가 HasinsuransurePolicyCheck을 지나갈 수 있다는 것입니다.
당신은 이것에 대한 다른 많은 옵션이 있습니다. 하나는 메시지 대기열을 사용하고 이러한 요청을 직접 처리하고 있습니다. 다른 하나는 사용하는 것입니다 sp_getApplock 그리고 그 패키지의 고유 한 키를 잠그십시오. 그렇게하면 더 이상 행이나 테이블을 잠그지 않아야합니다.
Aaronjensen의 응답에서 "메시지 큐"아이디어에 동의합니다. 동일한 데이터 행을 동시에 업데이트하려고 시도하는 여러 동시 스레드가 우려되는 경우 대신 스레드가 데이터를 작업 대기열에 삽입 한 다음 단일 스레드에 의해 순차적으로 처리됩니다. 대상 테이블은 "N"대신 하나의 스레드 만 업데이트되고 작업 대기열 작업은 메시징 스레드의 삽입으로 제한되고 데이터 처리 스레드의 읽기/업데이트로 제한되기 때문에 데이터베이스의 경합이 크게 줄어 듭니다.