2016-09-29 5 views
2

내 테스트에서 MariaDB을 사용하는 경우 일 때 REPETEABLE_READ 격리에서 동일한 쿼리를 실행해도 팬텀 읽기가 생성되지 않습니다.왜 팬텀 읽기를 생성하는 MariaDB에서 REPETEABLE_READ가 실행되지 않습니까?

예컨대

:

내가 BANK_ACCOUNT 테이블의 두 행이는 :

ID | OWNER | MONEY 
------------------------ 
    1 | John | 1000 
    2 | Louis | 2000 

예상 흐름은 아래와 같이 표시한다 :

THREAD 1 (REPETEABLE_READ)    THREAD 2 (READ_UNCOMMITED) 
    |           | 
findAll()->[1|John|1000,2|Louis|2000]  |   
    |           | 
    |          updateAccount(1, +100) 
    |          createAccount("Charles", 3000)     
    |          flush() 
    |           | 
    |           commitTx() 
    |           |_ 
    |           
findAll()->[1|John|1000,2|Louis|2000,  
    |   3|Charles|3000]     
    |           
    |           
commitTx()        
    |_           

Thread2.createAccount("Charles", 3000); 후에 정리해 스레드 1은 모든 행을 검색하여 얻을 것이다.

ID | OWNER | MONEY 
------------------------ 
    1 | John | 1000 
    2 | Louis | 2000 
    3 | Charles | 3000 

Thread1은 커밋되지 않은 변경 사항으로부터 보호되며 [1, John, 1100] 대신 [1, John, 1000]이 표시되지만 새로 삽입 된 행이 표시됩니다.

ID | OWNER | MONEY 
------------------------ 
    1 | John | 1000 
    3 | Charles | 3000 

그것은 팬텀 읽기가되지 않습니다

그러나, 무엇 Thread1 두 번째 findall은에서 검색하면 첫 findall은()의 것과 동일한 결과입니다. 왜?????

@Transactional(readOnly=true, isolation=Isolation.REPEATABLE_READ) 
@Override 
public Iterable<BankAccount> findAllTwiceRepeteableRead(){ 
    printIsolationLevel(); 
    Iterable<BankAccount> accounts = baDao.findAll(); 
    logger.info("findAllTwiceRepeteableRead() 1 -> {}", accounts); 
    //PAUSE HERE 
    ... 
} 

나는 그것이 //PAUSE HERE을 SAIS 실행을 일시 정지 :

이 Thread1에 의해 실행 된 코드입니다. 나는 (내가 스레드 거래를 업데이트 한
정말 뭘하는지와 흐름 :

bankAccountService.addMoneyReadUncommited(ba.getId(), 200); 
bankAccountService.createAccount("Carlos", 3000); 

을 그리고 Thread1이 다시 시작됩니다 :

그런 다음 Thread2는 실행

//PAUSE HERE 
... 
Iterable<BankAccount> accounts = baDao.findAll(); 
logger.info("findAllTwiceRepeteableRead() 2 -> {}", accounts); 

업데이트 새로운 행 삽입 후에 두 번째 트랜잭션을 커밋).

이것은 위키 백과에 따르면 유령 읽기이며 나는 매우 똑같은 시나리오라고 생각합니다. 그래서 난 아직도받지 못했습니다 이유를하지 않는 팬텀은 거래 과정에서, 두 동일한 쿼리가 실행될 때, 팬텀 읽기 [3|Charles,3000]

가 발생 읽고에 의해 반환 된 행의 컬렉션 두 번째 쿼리가 첫 번째 쿼리와 다릅니다.

SELECT ... WHERE 작업을 수행 할 때 범위 잠금을 얻지 못할 때 발생할 수 있습니다. 팬텀 읽기 이상은 트랜잭션SELECT ... WHERE 쿼리와 두 작업 사이에서 트랜잭션 2 이 (대상 테이블에서) 새 행을 생성 (즉, INSERT) 할 때 반복되지 않는 읽기의 특수한 경우입니다 (예 : ). WHERE 절을 수행하십시오.당신이 실제 행동으로 간주 무엇

Transaction 1        Transaction 2 
/* Query 1 */ 
SELECT * FROM users 
WHERE age BETWEEN 10 AND 30; 
              /* Query 2 */ 
              INSERT INTO users(id,name,age) VALUES (3, 'Bob', 27); 
              COMMIT; 
/* Query 1 */ 
SELECT * FROM users 
WHERE age BETWEEN 10 AND 30; 
COMMIT; 
+0

안녕하세요 Thilo, Thread1이 팬텀 읽기를 테스트 할 수있는 방법이 없습니다. – codependent

+0

필자는 유령이 읽은 것 같아요, 업데이트 된 행이 아니기 때문에 더티 읽기가 아니라 새 것으로 생각합니다. – codependent

+0

당신은 절대적으로 옳습니다. 감사 Thilo !! – codependent

답변

1

사실 repeatable_read에 대한 올바른 동작입니다. read_committed을 사용하면 예상되는 동작을 얻을 수 있습니다. repeatable_read에 mariadb 문서로

은 (굵은 글자는 광산이)라고 :

는 READ COMMITTED 격리 수준에서 중요한 차이가 있습니다 : 모든 일치가 가에 의해 설립 된 스냅 샷을 읽을 같은 트랜잭션 내에서 읽기 먼저을 읽으십시오.

스레드 1에서 첫 번째 FindAll() 콜을 리턴하는 호출이 스냅 샷을 설정했습니다. 두 번째 FindAll()은 단순히 동일한 스냅 샷을 사용했습니다.

Differences between READ-COMMITTED and REPEATABLE-READ transaction isolation levels에 Percona 블로그 게시물에 의해 뒷받침된다

REPEATBLE 자세히

, A는 뷰를 판독 '(trx_no가 trx_id> = ABC 보이지 않는 가 < ABB 본다) 상기 작성된 트랜잭션의 시작 및 읽기보기 (Oracle 용어의 일관성있는 스냅 샷)는 트랜잭션 기간 동안 열려 있습니다. 5시에 SELECT 문을 실행하고 을 열고 오후 5시에 열린 트랜잭션으로 돌아 오면 SELECT를 실행하면 5AM에서 본 결과 집합과 똑같은 결과 집합을 볼 수 있습니다. 이를 MVCC (Multiple Version Concurrency Control)라고하며 행 버전 및 UNDO 정보를 사용하여 을 수행합니다.

UPDATE

경고 : 다음 참조는 MySQL의 문서에서 있습니다. 그러나이 참조 문서는 innodb 저장소 엔진과 관련되어 있으므로 mariadb의 innodb 저장소 엔진에도 적용됩니다.

그래서 반복 가능한 읽기 분리 레벨에서 innodb 스토리지 엔진에서 논 - 로크는 첫 번째 읽기에 의해 설정된 스냅 샷에서 읽은 동일한 트랜잭션 내에서 선택합니다. 동시 커밋 된 트랜잭션에서 얼마나 많은 레코드가 삽입/업데이트/삭제 되었더라도 읽기는 일관성이 있습니다. 기간.

이것은 OP의 질문에서 설명한 시나리오입니다. 이것은 반복 가능한 읽기 격리 수준의 비 잠금 읽기가 유령 읽기를 생성 할 수 없다는 것을 암시합니다. 맞습니까? 정확히는 아닙니다. InnoDB Consistent Nonlocking Reads에 MySQL의 문서로

는 말한다 : 데이터베이스 상태의

스냅 샷은 내에서 거래를 문을 선택하려면 반드시 문을 DML에 적용됩니다. 또는 일부 행을 수정 한 후 해당 트랜잭션을 커밋 한 경우 다른 동시 REPEATABLE READ 트랜잭션 에서 발행 된 DELETE 또는 UPDATE 문이 이 쿼리 할 수 ​​없더라도 해당 커밋 된 행에 영향을 줄 수 있습니다.트랜잭션이 커밋 된 행을 다른 트랜잭션으로 업데이트 또는 삭제하면 해당 변경 사항이 현재 트랜잭션에서 볼 수있게됩니다.

SELECT COUNT(c1) FROM t1 WHERE c1 = 'xyz'; 
-- Returns 0: no rows match. DELETE FROM t1 WHERE c1 = 'xyz'; 
-- Deletes several rows recently committed by other transaction. 

SELECT COUNT(c2) FROM t1 WHERE c2 = 'abc'; 
-- Returns 0: no rows match. UPDATE t1 SET c2 = 'cba' WHERE c2 = 'abc'; 
-- Affects 10 rows: another txn just committed 10 rows with 'abc' values. 
SELECT COUNT(c2) FROM t1 WHERE c2 = 'cba'; 
-- Returns 10: this txn can now see the rows it just updated. 

은 요약하면 : : 예를 들어, 다음 같은 상황이 발생할 수 있습니다 당신은 반복 읽기 격리 모드와 이노를 사용하는 경우, 팬텀 동시 커밋 된 트랜잭션에서 데이터 수정 문이 상호 작용하는 경우 발생할 수있는 읽기 현재 거래 내에서 데이터 수정 문과 함께

격리 수준에 대한 링크 된 위키피디아 문서는 일반적인 이론 모델을 설명합니다. 차이가있을 수 있으므로 특정 기능이 어떻게 구현되는지 실제 제품 설명서를 항상 읽어야합니다.

위키 피 디아 (Wikipedia) 기사에서는 잠금 장치가 유령 읽기를 방지하는 수단으로 만 설명되어 있습니다. 그러나, innodb는 대부분의 경우에 팬텀 읽기를 막기 위해 스냅 샷 생성을 사용하므로 잠금에 의존 할 필요가 없다.

+1

@Shadow 지워 줘서 고마워. 제 예제에 기초하여 REPEATEABLE_READ와 SERIALIZABLE의 차이점을 알려주십시오. REPETEABLE_READ가 아직 수행하고 있지 않은 것에 대해이 마지막 것 하나가 보호 될 수 있습니까? – codependent

+0

@Shadow 질문을 업데이트했습니다. 나는 실제로 새로운 행 삽입 이후에 두 번째 트랜잭션을 커밋했다. 따라서 팬텀 정의에 따라 두 번째 트랜잭션에 나타나야한다. – codependent

+0

@Thilo 필자는 결코 그것을 쓰지 않았기 때문에 귀하의 의견을 이해할 수 없습니다. 이 예에서 OP는 스레드 2에서 트랜잭션을 커밋하고 thread1에서 두 번째 선택을 실행합니다. – Shadow