기존 Lock 기반 방식
| read | write | |
|---|---|---|
| read | O | X |
| write | X | X |
기존 컨커런시 컨트롤러는 위와 같이 read만 동시에 동작할 수 있기 때문에, read와 write가 동시에 발생하는 경우에는 대기하고 기다리게 됩니다. 이 때문에 동시에 처리하게 될 때 성능적으로 떨어지게 됩니다. 이를 해결하기 위해 MVCC가 등장하게 되었습니다.
MVCC
같은 데이터에 대해서
| read | write | |
|---|---|---|
| read | O | O |
| write | O | X |
양쪽 트랜잭션에서 write가 발생하는 경우에는 block되지만, 그 외의 경우에는 동작합니다. 이를 토대로 동시에 처리하는 성능이 올라가게 되는 것입니다.
- 트랜잭션 1은 유저의 권한을 읽습니다.
- 트랜잭션 2는 유저의 권한을 최고권한으로 변경합니다.
- 트랜잭션이 동작될 때 해당 값으로 변경되기 전에 write lock의 권한을 부여받습니다.
- 권한을 부여받으면 변경하려는 커밋 직전의 값(최고권한)을 따로 저장해둡니다.
- 트랜잭션 2가 write를 했지만 아직 커밋하지 않은 상태에서 트랜잭션 1이 해당 유저의 데이터를 읽으면 어떤 값을 읽게 될까요?
- 최종 커밋된 데이터를 읽게 됩니다. → 일반권한으로 읽게 되는 것입니다.
💡 즉 MVCC는 커밋된 데이터만 읽는다는 것입니다.
- 이제 트랜잭션 2가 커밋을 하게 되었습니다.
- 자체적으로 저장해두었던 최고권한 값이 데이터베이스에 반영됩니다.
- 2-a에서 부여받은 write lock 값이 unlock으로 회수됩니다.
- 여기서 왜 unlock으로 DB가 자체적으로 권한을 회수하는지 의문을 품을 수 있습니다. 이는 트랜잭션이 작동될 때 문제가 발생하면 롤백을 시켜주기 위해 해당 기능이 동작하는 것입니다. 따라서 recoverability를 위해 커밋할 때 write lock을 unlock 해줍니다.
- 여기서 트랜잭션 1이 해당 유저의 값을 읽는다면 어떻게 될까요?
- 여기서는 트랜잭션의 isolation level 값에 따라 다르게 읽게 됩니다.
- isolation level이 read committed라면 read하는 시간을 기준으로 그전에 commit된 데이터를 읽습니다. ⇒ 즉 최고권한 데이터를 읽게 된다는 것입니다.
- isolation level이 repeatable read라면 트랜잭션 시작 시간을 기준으로 그전에 commit된 데이터를 읽습니다. ⇒ 즉 기본권한 데이터를 읽게 됩니다.
- isolation level이 read uncommitted라면 MVCC는 commit된 데이터를 읽기 때문에 이 레벨에서는 보통 MVCC가 적용되지 않습니다.
- 여기서는 트랜잭션의 isolation level 값에 따라 다르게 읽게 됩니다.
MVCC
- 데이터를 읽을 때 특정 시점 기준으로 가장 최근에 commit된 데이터를 읽습니다. ⇒ Consistent read
- 데이터 변화(write) 이력을 관리합니다. ⇒ 추가적인 저장 공간을 더 사용하는 것이 단점이지만, 동시성 측면에서는 장점이 있습니다. 이 때문에 오늘날의 데이터베이스는 MVCC를 사용해서 만들어집니다.
- read와 write는 서로를 block하지 않습니다.
댓글
GitHub 계정으로 로그인하시면 댓글과 질문을 남기실 수 있습니다. 남겨 주신 글은 이 저장소의 Discussions에 쌓입니다.