
트랜잭션에서 서로 커밋하지 않은 데이터를 읽을 수 있는 격리수준
아래 그림을 보자. posts에 id가 1인 로우의 view_count가 10이라고 하자. 그림에서 1번 쿼리를 날리면 view_count가 10이 반환되고, 3번 쿼리를 날리면 view_count가 11이 반환된다.
이 격리 수준은 사용하기 어렵다. 왜냐하면 그림에서 Transaction1이 마지막에 커밋이 아니라 롤백을 했다면, Transaction2는 의미없는 값을 읽은 것이된다.
이렇게 커밋되지 않은 값을 읽을 수 있는 것을 dirty read라고 한다.

다른 트랜잭션에서 커밋된 데이터를 읽을 수 있는 격리수준
아래 그림을 보자. posts에 id가 1인 로우의 view_count가 10이라고 하자. 그림에서 1번 쿼리를 날리면 view_count가 10이 반환되고, 3번 쿼리를 날리면 transaction1이 커밋되기 전이므로 view_count가 10이 반환된다. 4번 쿼리를 날리면 transaction1이 커밋된 후이므로 view_count가 11이 반환된다.
이 격리수준에서는 한 트랜잭션 내에서 같은 값을 읽었는데 다른 값이 반환될 수 있다는 문제가 있다. 그림에서 transaction2에서 같은 쿼리를 날리는데 시점에 따라 값이 바뀌는 것을 볼 수 있다. 이러 문제를 non-repeatable-read라고 한다.
추가적으로 연속적으로 동일한 쿼리를 입력하는 중에 다른 트랜잭션에서 값을 Insert 해서 반환 값이 달라지는 문제는 phantom-read라고 한다.

한 번 읽은 데이터는 동일한 트랜잭션 내에서 항상 같은 값을 갖는 격리수준
이 격리수준은 InnoDB의 기본 격리수준이다. 이 격리수준은 다음과 같은 문제가 있다. 아래 그림을 보자. posts에 id가 1인 로우의 view_count가 10이라고 하자. Transaction1은 view_count를 11로 수정하고 커밋하는데 Transaction2도 view_count를 11로 수정하고 커밋해서 결국 view_count는 11이된다. 분명 프로그래머의
의도는 view_count가 12가 되도록 하는 게 목적이었을텐데 말이다. 이러한 문제를 lost update라고 한다.

lostupdate 문제는 해결 방법이 있다. select 할 때 뒤에 for update를 붙여서 조회를 하면 해당 로우에 락이 걸린다. 락이 걸리면 다른 트랜잭션에서 해당 로우를 읽거나 수정하지 못한다. 이를 배타락이라고 한다.
다른 트랜잭션에서 읽기는 가능하고 수정만 불가능하도록 락을 거는 공유락도 있지만, 근본적인 문제는 해결하지 못한다.
프로그래밍에서 해결할 수 있는 방법은 낙관적 락이 있다. version 컬럼을 만들어서 읽을 때 version과 수정할 때 version이 다르면 해당 트랜잭션을 롤백하고 다시 수행하는 방법이다.
트랜잭션이 하나씩만 수행되도록 하는 방법이다. 트랜잭션이 동시에 수행되지 않고, 순차적으로 하나씩 수행된다. 싱글 스레드처럼 움직인다는 말이다. Serializable 격리수준은 완벽한 데이터의 일관성과 정확성을 보장하지만 성능이 매우 떨어진다.