Skip to content

Conversation

@ehBeak
Copy link
Member

@ehBeak ehBeak commented Oct 9, 2024

안녕하세요! 재즈😊
2단계로 다시 뵙네요~

이전 로직이 2단계 미션의 요구사항을 거의 다 만족한 상황이라,
JdbcTemplate에서 가변인자를 초기화하는 로직을 클래스(ArgumentPreparedStatementSetter)로 옮긴 것밖에 변경사항이 없어요.

잘 부탁드립니다~!

@ehBeak ehBeak requested a review from seokmyungham October 9, 2024 09:58
@ehBeak ehBeak self-assigned this Oct 9, 2024
Copy link
Member

@seokmyungham seokmyungham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 배키~ 미션 잘 구현해주셨네요!! 간단한 리뷰사항과 질문들만 남겼습니다.
제가 궁금한게 많아서 배키가 가르쳐주셨으면 좋겠어요 😊

확인 부탁드리고 궁금하신 부분은 언제든지 연락주세요!
화이팅입니다 👍

}

public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... parameters) {
public <T> List<T> query(String sql, RowMapper<T> rowMapper, Object... args) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

query()update()에 발생하는 중복 로직을 개선할 수 있을 것 같아요!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PreparedStatementCallback<T> 를 사용해서 중복을 줄여봤습니다 :)

PreparedStatement pstmt = conn.prepareStatement(sql)) {

setParameters(pstmt, parameters);
ArgumentPreparedStatementSetter parameterSetter = new ArgumentPreparedStatementSetter(args);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PreparedStatementSetter 인터페이스가 아닌 구현체를 사용한 이유가 있을까요?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

앗 제 실수입니다. 수정했습니다~

* Serializable | | |
* Read Uncommitted | + | + | +
* Read Committed | - | + | +
* Repeatable Read | - | - | +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MySQL InnoDB 스토리지 엔진에서는 Repeatable Read 격리 수준에서도 팬텀 리드 현상을 방지할 수 있다고 하던데 이유가 궁금해요!!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MySQL InnoDB 스토리지 엔진에서는 Repeatable Read 격리 수준에서 MVCCNext-Key Locking이라는 기술을 사용하기 때문입니다.

MVCC는 언두로그에서 저장할 때 트랜잭션마다 고유의 ID를 줍니다. 그 ID를 기반으로 트랜잭션이 데이터를 읽어옵니다. 읽기 작업은 트랜잭션의 ID보다 작은 트랜잭션에서 수정된 데이터를 읽어오기 때문에 대부분의 팬텀 리드 현상을 방지할 수 있습니다~

mysql은 추가적으로 Next-Key Lock은 팬텀 리드 현상을 방지합니다. Next-Key Lock은 트랜잭션이 현재 읽는 범위와 다음 인덱스 레코드 사이에 락을 적용하는 것을 의미합니다.

예를 들어, ID가 1인 레코드와 3인 레코드가 있다고 가정하고 102 이상의 값에 락을 걸었다고 합시다.

Next-Key Lock이 없다면 이후에 id가 2인 값을 레코드로 넣을 수 있습니다. 그러나 Next-Key Lock이 사용된다면, id가 1인 레코드와 3인 레코드 사이(gap)에 락이 걸리기 때문에 2를 넣을 수 없습니다. 때문에 팬텀 리드 현상을 방지할 수 있습니다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

오 바로 이해했어요!!
자세한 설명 감사해요 배키 👍👍

* Serializable |
* Read Uncommitted | +
* Read Committed | +
* Repeatable Read | +
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

왜 이 상황에서는 팬텀 리드가 발생하게 된건지 이유가 궁금합니다 😊

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위 상황에 따르면,
(1) 먼저 A 트랜잭션에 id 1에 해당하는 레코드를 조회합니다. 이때는 조회만 하므로 테이블에 락이 걸리지 않습니다.
(2) 그 상황에서 트랜잭션 B가 2번 레코드를 insert 합니다. 그리고 트랜잭션 B가 종료(커밋)됩니다
(3) 트랜잭션 A는 id가 1 이상인 값을 업데이트합니다. (이때 락이 걸립니다.)
(4) 트랜잭션 A는 다시 1 이상인 값을 조회합니다. 락이 걸린 테이블은 이전에 트랜잭션 B가 insert한 값도 있기 때문에 2개의 값이 조회됩니다.

때문에 팬텀리드가 발생합니다~!

assertThat(actual)
.hasSize(0)
.containsExactly("");
.hasSize(1)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내부 논리 트랜잭션의 격리 수준이 Required 일 때, 내부 논리 트랜잭션에서 예외가 발생한다면 정상 처리와 예외를 방치할 때 각각 어떤 일이 발생하는지 궁금합니다!

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REQUIRED 전파 수준은 트랜잭션이 이미 시작된 상태라면, 그 트랜잭션에 합류하고 그렇지 않다면 새로운 트랜잭션을 시작합니다. 즉, 내부 논리 트랜잭션에서 REQUIRED를 사용하면 부모 트랜잭션과 같은 트랜잭션을 공유합니다~~~

정상 예외 처리인 경우
내부 논리 트랜잭션에서 예외가 발생하면, 부모 트랜잭션도 같은 트랜잭션으로 취급하기 때문에 부모 트랜잭션 전체가 롤백됩니다.

예외를 방치하는 경우
만약 예외가 발생하고 방치한다면 트랜잭션은 유지됩니다. 즉, 예외 발생 이후에도 작업이 계속 진행됩니다. 그렇게 되면 일부 데이터만 데이터베이스에 반영되는 등 문제가 발생할 수 있습니다.

assertThat(actual)
.hasSize(0)
.containsExactly("");
.hasSize(2)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

위와 반대로 내부 논리 트랜잭션의 격리 수준이 Requires New 일 때는 어떻게 되는지 궁금해요!!

Copy link
Member Author

@ehBeak ehBeak Oct 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내부 논리 트랜잭션의 격리 수준이 Requires New라면 부모 트랜잭션의 존재여부를 떠나서 항상 새로운 트랜잭션을 시작합니다. 만약 부모 트랜잭션이 존재한다면 해당 트랜잭션을 일시 중지하고 새로운 트랜잭션을 시작합니다.

정상 예외 처리인 경우
내부 트랜잭션에서 예외가 발생해서 롤백한다면, 내부 트랜잭션에서 수행된 작업만 롤백되고 부모 트랜잭션은 그대로 유지됩니다. 즉, 부모 트랜잭션에 영향을 주지 않습니다.

예외를 방치하는 경우
만약 내부 트랜잭션에서 롤백 처리를 하지 않는다면, 내부 트랜잭션이 롤백되지 않고 정상적으로 커밋됩니다. 부모는 이와 상관없이 �독립적으로 자신의 작업을 수행합니다.

Copy link
Member Author

@ehBeak ehBeak left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

재즈 안녕하세요~ 리뷰 반영해서 다시 요청드립니다!

재즈의 질문 덕분에 제대로 메타인지했네요..👍👍

Copy link
Member

@seokmyungham seokmyungham left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

안녕하세요 배키~ 고생하셨습니다!! 😀
2단계 목표와 학습 테스트 모두 잘 수행하신 것 같아요 👍👍
머지하겠습니다!

@seokmyungham seokmyungham merged commit c1a2fde into woowacourse:ehbeak Oct 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants