우아한 테크코스의 10분 테코톡을 정리한 글입니다
정의
Recored locking is the technique of preventing simultaneous access to data in a database,
to prevent inconsistent results.
DB의 일관성과 무결성을 유지하기 위해 트랜잭션의 순차적 진행을 보장할 수 있는 직렬화 장치
- Lock : 무엇인가 열리지 않도록 막는 장치
Lock이란?
DB에서는 Lock을 사용해 레코드(로우), 테이블, 테이블 전체의 접근을 막을 수 있다
- 위 그림은 하나의 DB에 두 유저가 동시에 접근하는 것을 그린 예제
- account는 계좌인데,
마이너스 통장
이 아니다. 마이너스 통장이 아니면 계좌는 0원 밑으로 떨어지면 안된다. - 두명이 계좌에 동시에 접근해서 (
동시성 문제
) 0원 미만인 -20원으로 떨어지는 side effect 발생
위와 같은 문제를 방지하고 데이터의 일관성
을 지키기위해 LOCK(잠금)을 걸고 관리하는 것을
Locking
이라고 한다.
필요성
동시성
동시에 접근하려 수정하려할 때 DB의 일관성이 깨질 수 있음.
송금 시스템, 도서 관리 시스템, 수강 신청 시스템, 멀티 트랜잭션 환경에서 필요
ex)
- 계좌 잔액이 3000원이고 A에게 5000원, B에게 3000원을 입금받았는데, 내 계좌에는 6000원만 남아있으면?
- -> 다수가 동시에 요청해서 생긴 동시성 문제, lock이 필요
책(토비의 스프링)
은 한권인데 두명이 동시에 빌린 것으로 되어버리면?- -> 동시성 문제, 책을 빌리는 기능에 lock이 필요하다
동시에 접근하여 수정하려고 할 떄 데이터베이스의 일관성이 깨질 수 있으므로
Lock
이 필요하다.
MySQL의 InnoDB Engine의 Lock의 종류
1. 배타 잠금(Exclusive Locks, X Lock)
- write에 대한 Lock( 쓰기에 대한 락 )
SELECT ... FOR UPDATE
나UPDATE
,DELETE
등의 수정, 삭제 쿼리 등에 exclusive Lock을 걸 수 있다.- exclusive lock이 걸려있을 경우 shared lock, exclusive lock 둘다 걸 수 없다. (쓰는 중 읽기, 쓰기, 수정, 삭제 불가능)
- 락을 중복으로 걸 수 없다.
2. 공유 잠금(Shared Lock, S Lock)
- READ에 대한 lock (읽기에 대한 락.
- 일반적인 select 쿼리에 사용하는 것이 아닌 SELECT ... FOR SHARE(MySQl 8.0 버전 부터) 등 일부 쿼리는 READ 작업 수행시 lock을 건다.
- 또는 SELECT ... LOCK IN SHARE MODE
- shared lock이 걸려있을 경우 exclusive lock을 걸 수 없다. (읽는 중 수정, 삭제가 불가능하다)
- 여러 transaction이 동시에 한 row에 shared lock을 걸 수 있다.
3. 레코드 락 (Record Lock, InnoDB에서 다른 DBMS와는 다르게 제공하는 락 )
- InnoDB에서 Record Lock은
row가 아닌 DB index record
에 걸리는 Lock - 여기도 동일하게 shard lock, exclusive lock 두종류가 존재한다.
- 테이블에 index가 없다면?
- 숨겨져 있는 clustered index를 사용하여 record를 잠근다.
4. 갭 락 (Gap Lock)
- index record의
gap
에 걸리는 lock- gap : index record가 없는 부분. 특정 범위의 레코드
- 만약 id가 1,2,7,8인 칼럼이 존재하고 id 칼럼에 index가 걸려있다면 2 < id < 7 에 gap lock을 걸어 새로운 row가 추가되지 못하도록 막는다.
- 조건에 해당하는 새로운 row가 추가되는 것을 방지하기 위한 것
- record lock은 이미 존재하는 row가 변경되지 않도록 lock을 거는 반면,
- gap lock은 새로운 row가 추가되는 것을 방지하기 위함
DB Deadlock 해결 방법
- Deadlock이 걸렸을 때 Log를 확인하려면
SHOW ENGINE INNODB STATUS;
명령어
- MySQL이나 오라클은 deadlock detection 이나 lock wait timeout을 사용하여 해결한다
- deadlock detection 이 활성화 되어있으면 rollback할 작은 트랜잭션을 선택한다.
- 트랜잭션의 크기는 insert, update, delete 된 행 수에 의해 결정하며, 오라클에서는 반드시 그렇지는 않다고 한다.
- 즉 벤더사마다 다를 수가 있다.
- 트랜잭션의 크기는 insert, update, delete 된 행 수에 의해 결정하며, 오라클에서는 반드시 그렇지는 않다고 한다.
- deadlock detection 활성화 되어있지 않으면, lock wait timeout으로 해결한다
우리(백엔드 개발자들)가 Lock을 알아야 하는가?
- DBA 있는데 왜 알아야해?
- 난 안쓰고 개발 잘 했는데?
- 그냥 알고만 있으면 되지? 쓸일은 없지
다음과 같은 프로그램 개발한다면 무조건 알아야 한다
- 돈 관련 송금 시스템
- 도서 관리 시스템 (재고 관리)
- 수강 신청
- 멀티 트랜잭션 환경
종류와 사용법
Optimistic Lock(낙관적 락)
- 낙관적인 : 기본적으로 데이터 갱신시 충돌(동시성 문제)이 발생하지 않을 것이라고 낙관적으로 보는 것
- 비선점적인 : 낙관적으로 예상하기 때문에 우선적으로
lock
을 걸지 않음
그림을 통해 살펴보자.
- 그림 - Optimistic Lock의 동작 원리
- version 이라는 컬럼을 이용해서 관리
- DB단에 관계 없이 어플리케이션 레벨에서 Lock을건다.
- 충돌 방지 (충돌이 안나진 않는다. )
Optimistic Lock
은버전
을 사용하여 관리를 한다.
- 그림과 같이
Client1
이Order
에 접근을 할 때Version 1
의Order
에 접근한다. - 동시에
Client2
도Version 1
의Order
에 접근한다. Client1
이Order
를 수정하거나 추가하고Commit
을 자신이 접근한Version 1
에 +1을 하여Version 2
로 날리게된다- 이제
DB
의Order
버전은 '2'이다.
- 이제
Client2
도Order
에 어떤 것을 추가하고 마찬가지로Version 2
로Commit
을 날리는데, 이미Order
의 버전은2
이기 때문에OptimisticLockException
을 발생시킨다.
JPA
를 사용할 때의 예시 코드
@Entity
public class OptimisticLockingStudent {
@Id
private Long id;
private String name;
private String lastName;
@Version
private Integer version;
}
- @Version 어노테이션사용. 알아서 동작한다.
- 충돌이 일어나면
OptimisticLockException
이 발생한다
위와 같이 @Version
을 사용한 필드를 두게되면 자동으로 commit
시에 버전이 올라가고 충돌을 방지해준다.
데드락 가능성이 적고 성능의 이점을 가짐
하지만 충돌이 발생하면 오버헤드가 발생
Pessimistic Lock(비관적 락)
- 비관적인 : 기본적으로 데이터 갱신시 충돌이 발생할 것이라고 비관적으로 보고
lock
을 설정 - 선점적인 : 비관적으로 보기 때문에, 우선적으로(조회할 때부터)
lock
을 검
조회를 할 때부터 lock
을 걸어 다른 곳에서 접근할 시에 wait time
이 발생
여기에는 두가지 동류가 존재하는데 Shared Lock
과 Exclusive Lock
이 존재한다.
- Shared Lock (읽기 락이라고도 한다): 다른 사용자가 동시에 읽을 수는 있지만, 수정/삭제를 방지
- Exclusive Lock (쓰기 락이라고도 한다.): 다른 사용자의 읽기/수정/삭제를 모두 방지
@Entity
public class PessimisticLockingStudent {
@Id
private Long id;
private String name;
}
#----------------------------------
PessimisticLockingStudent student = entityManager.find(PessimisticLockingStudent.class, 1L);
entityManager.refresh(student, LockModeType.PESSIMISTIC_WRITE); // Exclusive Lock
PessimisticLockingStudent student2 = entityManager2.find(PessimisticLockingStudent.class, 1L);
entityManager2.refresh(student2, LockModeType.PESSIMISTIC_WRITE); // 조회에서 예외 발생
- PESSIMISTIC_WRITE 모드 일때 Exclusive lock이 걸리고, 다른 트랜잭션이 조회하려고 하는 순간 바로 예외가 터진다
- 다른트랜잭션이 find() 하는 순간
PerssimisticLockException
예외 발생
- 다른트랜잭션이 find() 하는 순간
충돌에 대한 오버헤드가 줄어들고, 무결성을 지키기 용이
충돌이 없으면 오버헤드가 발생
간단 정리
Optimistic Lock(낙관적 락) | Perssimistic Lock (비관적 락) | |
---|---|---|
정의 | 충돌이 없을것이라 예상한다. | 충돌을 예상하고 미리 락을 건다. |
사용법 | JPA를 사용한다면 @Version 또는 동작 원리가 단순하니 직접 만들어도 된다. |
Mode 설정 및 쿼리에 직접 사용 DB단에서도 설정 가능하다. |
별명 | 낙관적 락 / 비선점 락 | 비관적 락 / 선점적 락 |
장점 | 데드락 가능성이 적으며 성능의 이점 | 충돌에 대한 오버헤드가 줄어든다. 무결거성을 지키기 용이하다. |
단점 | 충돌이 발생하면 오버헤드가 발생한다. | 충돌이 없으면 오버헤드가 발생한다. |
적용
즉, 어떤 상황인지를 판단하는 것이 중요하다 . (Lock은 비용이 비싸니까)
- 충돌이 자주 발생하는 상황인가?
- 읽기와 수정의 비율이 어디에 가까운가?
일반적으로 웹 애플리케이션은 Optimistic Lock
을 주로 사용한다. (읽기가 더 잦기 때문에)
추가로 공부하면 좋을 내용
- 도메인에 대한 Lock 걸어야하나? 말아야하나?
- PessimisticLock Scope - NORMAL/EXTENDED
- 연관관계에 대해서 게시물에 대해 댓글을 가져올 때 같이 락을 걸 것인가?
- 락이 결국 트랜잭션이랑 다 연관되서 사용된다.
- Lock 관련 에외 처리 전략
- 다양한 상황 연출해보기
참조
'하루한개 > 우아한 테크코스-10분테코톡' 카테고리의 다른 글
디디의 Redis (0) | 2023.03.21 |
---|---|
Spring Transation (0) | 2022.09.06 |
정규표현식 and With Java (0) | 2022.08.31 |
캐시(Cache), 브라우저 캐시, Spring Boot Cache (0) | 2022.08.21 |
Spring vs Spring boot (0) | 2022.08.21 |