2013-07-23 1 views
1

저는 간단한 스크립트를 실행하여 무결성 문제를 해결할 수있는 솔루션을 테스트하고 있습니다. 나는 테이블 my_table왜 업데이트 예제 작업을 선택합니까?

|foo  | 
|1  | 

이 그리고이 두 조각이 있다고 가정 :

// db_slow.php 
<?php 
$db = new PDO('mysql:host=localhost;dbname=my_playground;charset=utf8', 'root', ''); 
echo 'starting transaction<br />'; 
$db->beginTransaction(); 
$stmt = $db->query('select * from my_table for update'); 
$rows = $stmt->fetchAll(); 
echo 'count tables: ', count($rows), '<br />'; 
if (count($rows) == 1) { 
    sleep(10); 
    $db->query('insert into my_table(foo) VALUES(2)'); 
} 
$db->commit(); 
echo 'done'; 

// db_fast.php 
<?php 
$db = new PDO('mysql:host=localhost;dbname=my_plyaground;charset=utf8', 'root', ''); 
echo 'starting transaction<br />'; 
$db->beginTransaction(); 
$stmt = $db->query('select * from my_table for update'); 
$rows = $stmt->fetchAll(); 
echo 'count tables: ', count($rows), '<br />'; 
if (count($rows) == 1) { 
    $db->query('insert into my_table(foo) VALUES(3)'); 
} 
$db->commit(); 
echo 'done'; 

db_slow.php이 경쟁 조건을 시뮬레이션하기 위해 10 초 지연이 있습니다.

내가 알고 있듯이 select ... for update은 선택한 모든 행을 잠급니다. db_slow을 실행하면 db_fast이 표시되고 이 예상대로 대기하므로 db_fast도 10 초 지연됩니다.

// db_slow.php 
starting transaction 
count tables: 1 
done 

// db_fast.php 
starting transaction 
count tables: 2 
done 

그리고

|foo  | 
|1  | 
|2  | 

my_table 내가, select ... for update 잠금이 선택하는 모든 행을 알고있는 것처럼 :

그러나, 내가 무엇을하지 않는 것은이 출력된다 트랜잭션. 행 1을 선택하려고,이 잠겨 있음을 참조하십시오

  1. db_slow : 선택 행 1과는
  2. db_slow 잠금 : 그것은 단지 1 행 있다고보고
  3. db_fast 기다려 그래서 내가 기대하는 것입니다
  4. db_slow을 기다립니다 '2'
  5. db_fast에 행을 삽입 : 1 행 잠금이 해제되기 때문에 계속
  6. db_fast : 만이 삽입하므로, 1 개 행을 선택한 '3'
  7. 끝을로

위에서 설명한 출력과 지연은 단계 1, 2, 3을 확인하는 것 4. 잠금을 확보하려고 후 선택 실행 db_fast 것 같아? 한 행을 선택한 다음 잠 그거나 기다릴 것이라고 생각했습니다.


다소 관련 질문 : select ... lock in share mode 이것을 실행하면

나는

// db_slow.php 
starting transaction 
count tables: 1 
done 

// db_fast.php 
starting transaction 
count tables: 1 
done 

그리고 my_table

|foo  | 
|1  | 
|3  | 

db_slow 행을 '삽입되지 끝낼 2 '1 행만 있다고 생각할 때조차도 테이블 (행을 삽입하는 조건)?

+0

이 코드는 어딘가에 있어야합니다. – sgroves

+0

하나의 연결은 my_playground에 연결되고 다른 연결은 adrian_playground_alpha - typo입니까? – Alden

+0

@sgroves 이런 쓰레기는 이전에 '공유 모드에서 잠금'을 시도했지만 다시 변경하는 것을 잊어 버렸습니다. 그것은 당황 스럽지만, 비슷한 것을 업데이트 용으로 바꿀 때 일어납니다. @Alden 미안하다. 같은 데이터베이스에서 실행 중이며 여러 스크립트가 있고 여기에 붙여 넣은 모든 불일치를 수정하는 것을 잊어 버렸다. – Raekye

답변

1

예상되는 동작이 다소 벗어난 것 같습니다. db_slow가 커밋되기 전에 테이블의 모든 행이 잠겨 있습니다. 커밋 한 후 두 행이 있습니다. db_slow가 커밋되면 db_fast가 차단 해제됩니다.따라서, 동작은 다음과 같습니다

  1. db_slow : 선택 행 1, 그것을
  2. db_slow을 잠글 : 그것은 단지 1 행 있다고보고
  3. db_fast을 기다립니다 : 1 행을 선택하려고,이 잠겨 볼, 대기
  4. db_slow는 : 커밋
  5. db_fast : '2'
  6. db_slow에 행을 삽입 차단을 해제하고 2 행
  7. db_fast 읽기 : 아무것도에게
  8. 하지 않습니다
  9. foo : 1, 2로 끝납니다.
+0

3 단계 및 6 단계; 그래서 잠금을 얻지 못하면 기다리는 동안 전체 쿼리를 재실행합니까? 원래 예상했던 행에 대해 업데이트 된 값이 있는지 확인하기 만하면됩니다. 그러나 이것이 맞는 경우라면 의미가 있습니다. – Raekye

+0

@Raekye 쿼리가 대기 할 때 실행이 차단되었습니다. 실제 동작은 * 전체 쿼리를 다시 실행하지는 않지만 원래 쿼리를 계속 * 수행한다는 것입니다. SQL은 필수 언어가 아닌 선언적 언어입니다. 테이블을 여러 부분으로 나눌 수 없습니다. 정상적인 (커밋되지 않은 읽기가 아닌) 상황에서는 "원래 예상했던 행에 대한 업데이트 된 값"과 같은 동작을 얻지 못합니다. SQL의 핵심은 전체 트랜잭션이 * atomic *입니다. 그것은 그것의 원자가되지 않았을 것입니다 그것은 단지 거기에 있었어야했다 읽습니다. –

+0

@Raekye 트랜잭션 격리 수준을 직렬화 할 수있는 이유를 쉽게 알 수 있습니다. 의미에는 차이가 있지만,이 경우에는 차이가 없으므로 직렬화 가능하다고 가정 해 봅시다. 직렬화가 가능할 때, 두 번째 쿼리가 시작되기 전에 하나의 쿼리가 먼저 수행되는 순서가 존재합니다. 귀하의 경우에는 db_fast가 시작되기 전에 db_slow가 완전히 커밋됩니다. –