일반 메모장 같은걸로도 저장은 할 수 있다. 근데 하필 왜 데이터베이스를 쓰는가?
그건 트랙젝션을 지원하기 때문이다. 거래인데, 안전하게 거래를 해주는걸 보장해준다.
애초에 거래라는게 하나처럼 보여도 사실 여러개다. 예를들어 A가 B에게 5000원을 입금했을 때 현실로는 그냥 지폐 5000원 주면 되지만 디지털로는 A의 계좌에 5000원을 감소시키고 B의 계좌에 5000원을 증가시키는 2가지 일을 동시에 수행한다. 그럼 만약 A의 계좌에 5000원을 감소시키는건 성공했지만 B의 계좌에 5000원을 증가시키는걸 실패했다면?? 걍 중간에 돈이 날라가고 매우 큰 법적 문제가 생길 수 있음. 그래서 이런 거래를 하나의 작업처럼 만들어서 정상 반영하는걸 커밋(commit)이라고 하고 하나라도 실패해서 거래 이전으로 되돌리는걸 롤백(rollback)이라고 한다.
위에서 말한 예시를 구체적으로 조건을 정하면 원자성, 일관성, 격리성, 지속성으로 나눌 수 있다. 트랙젝션은 이 4가지를 보장해야 한다.
이 4가지에서 서버 성능에 문제가 되는건 격리성인데, 격리성을 너무 철저하게 지킨다고 하나의 데이터를 다른 애가 보는동안 못 보게 하면 병목현상이 매우 심하게 일어날거다. 그렇다고 아직 커밋되지 않은 걸 읽게하면 불안하다. 그래서 보통 커밋된 읽기나 반복 가능한 읽기 정도로 타협을 본다고 한다.
커넥션으로 연결 시 서버에서도 커넥션을 맺고 안에서 세션이 만들어진 뒤 그 세션을 통해 명령이 수행되는 방식이다. 서버처럼 한번에 여러개 만들어 줄 수도 있다.
코드 작성 전에 개념을 간단히 보자
아무것도 하지 않은 상태. 테이블을 조회하면 모두에게 같은 테이블이 보여진다.
이제 막 추가했지만 아직 커밋은 하지 않은 상태. 커밋을 하기 전에는 임시로 저장한다. 데이터를 추가하고 있는 사람에게는 자신이 추가한 내용이 보이지만 다른 사람에게는 보이지 않는다. 이전까지 그냥 데이터베이스에 추가할 수 있었던건 자동 커밋 기능을 사용하고 있었기 때문. 만약 아직 커밋도 하지 않았는데 조회가 가능하면 저장 안되고 롤백 되었을 때 큰 문제가 될 수 있다. 설정할 순 있지만 위험함.
이렇게 커밋을 해야 임시로 저장되었던게 실제로 반영되어서 다른 사람도 볼 수 있다.
롤백했을 때는 트랜잭션을 시작하기 바로 직전 상태로 되돌아간다.
db에서도 직접 autocommmit을 할지 안할지 설정할 수 있다. 설정해도 바꿀 수 있다. 다만 autocommit을 false로 설정할 경우, 작성 후에 반드시 commit이나 rollback을 해줘야 한다. 아무것도 안하고 시간이 지나면 timeout되서 자동 rollback됨.
이 set autocommit false 설정은 한 번 지정하면 계속 적용되지만, 자신이 실행하려는 작업 위에 작성해서 명시적으로 트랜젝션을 시작한다고 표시하기도 한다.
두 개의 세션을 띄워서 비교했을 때, 처음 추가했을 때는 autocommit을 킨 상태로 추가해서 둘 다 들어가있지만 두번째에선 autocommit을 해제한 상태로 한 쪽에서만 추가해보았다. 위에서 말한건 처럼 실제로 추가한 쪽만 조회되고 다른 세션에선 추가가 안된것으로 보인다.
rollback을 했을 경우 입력했던 데이터는 지워지고, commit을 했을 경우 입력했던 데이터가 실제로 반영되어 다른 세션에서도 변화된 것을 확인할 수 있다. 지금은 데이터를 추가 삭제한거라 별 감흥이 안오지, 계좌이체같은 데이터 업데이트면 감이 온다.
이게 초기 상태.
만약 명령어를 오타없이 잘 쳤을 때 위에서 본 것 처럼 commit을 하느냐 rollback을 하느냐에 따라 실제로 적용되고 안되고 차이가 있다.
만약 오타가 나서 오류가 났을 때 강제로 커밋을 할 수는 있다. 하지만 그런걸 원한 게 아니므로 에러가 뜨면 rollback을 직접 해줘야 한다.
트랜잭션 덕분에 두 개의 명령을 마치 하나의 명령처럼 다룰 수 있게 되었다. 즉 오류가 나면 그 명령들이 없었던 것 처럼 한번에 되돌리고, 성공하면 잘 갱신되게 할 수 있다. 마치 하나의 것처럼..
따라서 이런 중요한 명령 같은건 꼭 트랜잭션을 사용한다.
다음은 락을 알아본다.
그럼 autocommit을 false로 하고 한쪽에서 작성중인데 다른 한쪽이 같은 데이터를 수정한 뒤 commit하면 어떻게 될까? 바로 꼬인다. 예를 들에 잘못되어서 rollback을 할 경우 누가 작성한 것을 기준으로 rollback을 해야하는지, 아니면 commit을 할 경우 누가 들어온 대로 commit을 해야 하는지.. 등 꼬인다.
그래서 동시에 작성하는것 자체를 예방하려고 락 개념이 나왔다. 한 쪽이 수정중이면 다른 한쪽은 접근을 못하게 막아 이렇게 동시에 수정하는 일 자체를 차단하는 것이다.
조금이라도 더 빨리 접근한 쪽이 락을 획득한다. 다른 쪽은 끝날때까지 기다린다.
커밋을 통해 자기 작업이 끝났음을 알리면 락을 다시 반납해서 다른 사람도 수정할 수 있게 한다. 아까 아주 조금 늦게 요청해서 기다린 사람이 락을 획득하면서 자신의 작업을 수행한다.
이제 락을 실습해보자.
먼저 한 쪽에서 이미 데이터를 업데이트하고 있고 아직 commit이나 rollback을 하지 않아 작성중인 상태에서 다른 쪽이 업데이트하려고 하니 명령어가 실행되지 않는다. 명령이 안먹히는게 아니라 다른 쪽에서 락을 가지고 있어서 자신에게 락이 올 때까지 기다리고 있는 것이다.
실제 commit등으로 작업을 완료했음을 알리며 락을 반납하면 대기하고 있던 쪽이 락을 얻어 수정한다.
그래서 현재 상황은 이미 한쪽에서 수정된 것이 데이터베이스에 반영된 상태고, 다른쪽은 그렇게 변경된 값에서 자신이 변경한 갑을 자신만 막 업데이트 한 상태다. 여기서 다른쪽이 commit을 하면 그 다른쪽이 변경한 사항이 적용된다.
만약 timeout때 까지 끝까지 락을 안돌려주면? 기다리던 쪽에서 락 타임 오류가 발생한다.
일반적으로 조회를 할 때는 락이 필요하지 않아 가져오지 않지만 for update를 사용하면 조회할 때도 락을 가져올 수 있다. 중요한 정산 시간대에 자기가 보고 있는데 값이 막 변하면 제대로 된 계산을 못하도록 막을 때 사용할 수 있다. 물론 조회 자체는 되지만 락이 없기 때문에 변경이 안된다.
이것도 for update를 사용했다면 commit을 입력해야 락을 반환하는 방식이다.
이제 프로젝트 코드에 적용해보자.
아직 트랜젝션 사용하기 전이다. 아니 정확히는 사용하고 있지만 오토로 설정되어 있어서 한줄 할 때마다 실행되어 저 계좌이체를 하는 두 가지를 실행하는걸 하나로 하는 원자성이 보장이 안된다는 것이다.
그래서 아직 계좌이체를 하는 전체 함수 자체를 트랙젝션으로 묶지 않아서 중간에 실패해도 돌아오지 않고 그대로 감소한 채로 저장되는 심각한 문제가 발생한다.
참고로 테스트할 때 DB랑 연동해서 하기 때문에 각 테스트가 끝날때마다 clear 해줬는데 트랜젝션으로 바로 이전으로 되돌리는게 좋은 방법이다. 이건 한참 후에 해본다.
트랜잭션 시작점은 보통 해당 비즈니스 함수 실행 전에 한다. 그리고 어디서부터 트랜잭션 지점을 시작하는지 명시적으로 setAutoCommit(false)을 적어서 겸사겸사해서 표현하는 편이다.
주의할 점은 반드시 같은 커넥션을 유지해야 한다. 그래서 보면 같은 커넥션 유지하려고 커넥션을 매개변수로 받아서 안에서 수행하고 막 난리도 아니다.
커넥션을 하나로 유지해야 하므로 이 레퍼지토리를 사용하는 서비스에서 커넥션을 관리할거다.
이제 서비스에서 커넥션을 관리하고 여기가 트랜젝션 비즈니스 함수 시작 기준점이기 때문에 여기서 연결하고 하느라 try catch 범벅에 좀 복잡해졌다. 어쨌든 구성은 이게 맞으니 옛날엔 이렇게 했다고 한다.
이젠 실패했을 시 롤백 기능이 있어 중간에 실패해도 원래대로 돌아온다.
'CS > 김영한 스프링 강의' 카테고리의 다른 글
스프링 DB 1편 - 데이터 접근 핵심 원리 - 섹션5. 자바 예외 이해 (0) | 2023.08.15 |
---|---|
스프링 DB 1편 - 데이터 접근 핵심 원리 - 섹션4. 스프링과 문제 해결 - 트랜잭션 (0) | 2023.08.13 |
스프링 DB 1편 - 데이터 접근 핵심 원리 - 섹션2. 커넥션풀과 데이터소스 이해 (0) | 2023.08.12 |
스프링 DB 1편 - 데이터 접근 핵심 원리 - 섹션1. JDBC 이해 (0) | 2023.08.12 |
스프링 MVC 2편 - 백엔드 웹 개발 활용 기술 - 섹션11. 파일 업로드 (0) | 2023.08.11 |