2009-06-01 4 views
3

MySQL과 PHP + Propel 1.3을 사용하는 동안 동시성 문제가있는 것 같습니다. 아래는 Propel 객체의 "save"메소드의 작은 예제입니다.동시성 문제

public function save(PropelPDO $con = null) { 
    $con = Propel::getConnection(); 
    try { 
     $con->beginTransaction(); 
     sleep(3); // ignore this, used for testing only 
     parent::save($con); 
     $foo = $this->getFoo(); // Propel object, triggers a SELECT 

     // stuff is happening here... 

     $foo->save($con); 
     $con->commit(); 
    } catch (Exception $e) { 
     $con->rollBack(); 
     throw $e; 
    } 
} 

문제는 $ foo 개체입니다. 아주 짧은 시간에 예제 메소드를 차례로 두 번 호출한다고 가정 해 봅시다. 첫 번째 트랜잭션이 그것을 저장할 수있는 기회가 전에 어떤 경우에는 두 번째 트랜잭션이 ... ... ...

$foo->save($con); 

... $ foo는을

$foo = $this->getFoo(); 

을 $ foo는을 읽는 경우 두 번째 거래가 읽히고 오래된 것이되고 나쁜 일이 일어날 것입니다.

테이블의 잠금을 강제로 수행 할 수 있습니다. Foo 오브젝트가 저장되어 후속 트랜잭션이 첫 번째 작업이 완료된 후에 만 ​​읽을 수 있도록합니까?

편집 : 컨텍스트는 웹 응용 프로그램입니다. 간단히 말해서 어떤 경우에는 데이터 수정 ($ foo의 페치와 저장 사이에서 발생하는)을 가장 먼저 요청하기를 원합니다. 모든 후속 요청은 수정을 할 수 없어야합니다. 수정이 발생할지 여부는 가져온 $ foo 상태 (테이블 행 속성)에 따라 다릅니다. 두 개의 트랜잭션이 동일한 $ foo를 가져 오는 경우 수정이 두 번 발생하여 문제가 발생합니다.

+0

루프 등에서이 저장 메서드를 호출하고 있습니까? 아니면 두 명의 사용자가 본질적으로 동시에이 리소스에 접속했기 때문에이 문제가 연결간에 문제가되는 것입니까? –

+0

후자의 경우. – Ree

+0

브라우저 기반 응용 프로그램 인 경우 동시에 사용할 필요는 없습니다. 이 예제를 보자. 사람 A와 B는 어느 시점에서 같은 데이터 행을로드한다. 사람 A가 변경하고 저장하고 화면을 새로 고침하며 데이터가 좋아 보인다. 사람 C가 화면을로드하고 사람 A의 변경을 확인합니다. 사람 B가 마침내 데이터를 저장합니다. 사람 A는 데이터를로드하지만 변경 사항이 누락되었습니다. –

답변

1

이 기존 행을 화면/응용 프로그램에로드 할 때 LastChgDate도로드하십시오. 저장하면 "LastChgDate = thevalue"을 사용하십시오. 업데이트의 영향을받은 행 수를 확인합니다. 0이면 "다른 사람이 이미이 레코드를 저장했습니다"라는 오류를 반환하고 롤백 및 기타 변경 사항을 반환합니다. 이 논리를 사용하면로드 할 때와 동일한 경우에만 행을 저장할 수 있습니다. 새 행 인 INSERT의 경우 새 행이므로 필요하지 않습니다.

+0

하지만, 지금은 스키마가 바뀌기를 꺼리고 Propel이 ORM이기 때문에 제안한 것을 성취하기 위해 "굴절"시켜야 할 것 같습니다. 내가하기 전에, "상위 레벨"솔루션이 있는지 궁금 하네. 분명히 트랜잭션 중에 테이블을 잠글 수있는 방법이 있어야합니까? 트랜잭션을 시작한 직후 'LOCK TABLE'을 시도했지만 어떤 이유로 든 도움이되지 않았습니다 ... – Ree

+0

@Ree, 마지막으로 변경 한 날짜 속성을 추가하거나 다른 속성과 함께로드 할 수는 없습니까? 행을 잠그지 만, 잠금이 해제되면 JUNK로 겹쳐 쓸 수 있습니다.나는 응용 프로그램에 유용 할 수 있기 때문에 마지막으로 변경된 날짜를 사용하는 것을 좋아합니다. 동일한 일을 할 수는 있지만 타임 스탬프 데이터 유형의 열을 사용할 수는 있지만 표시 할 때 유용하지 않거나 사용자가 언제 " 변경됨 ". –

+0

업데이트 된 $ foo가 읽은 버전과 같은지 확인하여이 문제를 해결했습니다. 이 해답을 솔루션의 기반으로 사용했기 때문에이 해답을 최상의 솔루션으로 지정했습니다. – Ree

0

MySQL에서는 SELECT FOR UPDATE를 사용하여 잠금을 수행 할 수 있다고 생각합니다.

또 다른 옵션은 GET_LOCK 및 RELEASE_LOCK MySQL 함수 호출을 사용하여 리소스에 대한 액세스를 제어하는 ​​데 사용할 명명 된 잠금을 만드는 것입니다.

이러한 접근 방식에는 몇 가지 단점이 있습니다. 나는 그들 자신을별로 사용하지 않았고 그들은 MySQL에 특화되어 있지만 그들은 당신을 위해 일할 수 있습니다.

+0

행을 잠글 수 있지만 업데이트를 완료하면 다른 사용자가 오래된 데이터가 오래된 데이터 –

+0

잠금을 사용할 수없는 경우 차단하는 대신 사용자에게 돌아가서 다른 사용자가 행을 편집 중이며 행이 사용 가능하게되고 업데이트되었을 때 데이터가 다시 표시된다고 알립니다. – RibaldEddie

+0

아마도이 경우 스레딩을 사용해야 할 것입니다. – RibaldEddie