스프링데이터 + JPA/스프링 데이터 JPA

19. JPA 힌트, Lock

sdafdq 2023. 11. 23. 12:02

먼저 JPA 힌트란,

 

SQL에는 Hint 라는 것이 있다.

그러니까,

SELECT employee_name FROM employees USE INDEX(employee_idx) WHERE department_id = 10;

Hint 라는 것은 어떤 명령을 처리하기 위해 최적화된 방법?을 지시해 주는 것이다.

mysql에서는 저렇게 use index로 힌트를 주로 제공한다고 한다.

 

use index말고도 여러 힌트들이 있다고 한다.

USE INDEX : 사용할 인덱스를 지정합니다.
FORCE INDEX: USE INDEX와 비슷하지만 더 강력합니다. 비용을 고려하지 않습니다.
IGNORE INDEX: 최적화 프로그램에 특정 인덱스를 무시하도록 지시합니다.

 

여튼, sql문에 쿼리 실행 방법에 대해 뭔가 최적화를 위해 미리 만들어 놓은 프로세싱 패턴을 지시하는 것이다.

 

JPQL에서도 그런 게 있다.

@QueryHints(value = @QueryHint(name = "org.hibernate.readOnly", value = "true"))
Member findReadOnlyByUsername(String usernane);

이것이다.

@QueryHints라고 해서 안에 @QueryHint들을 넣어주면 된다.

그 중 하이버네이트가 제공하는 readOnly를 true로 해 줬다. 즉, 사용하기로 지정하면서 열어줬다.

나는 처음에 value="true"하길래 왜 저 쿼리 힌트쓴다고 명시해 주면 이미 저 힌트를 쓴다는 건데 왜 true를 넣어줘야 하는 거지? 라고 생각했는데,

false로 넣어준 것과 아예 저 QueryHint를 사용하지 않는 것에 처리하는 과정의 차이가 있다고 한다... 라고 하는데 맞나? chat GPT가 이랬다 저랬다 해서 모르겠다.

https://www.inflearn.com/questions/1083044

물어봤다.

 

여튼,

저렇게 하이버네이트에서 제공해주는 readOnly를 쓰면 어떻게 되냐면,

우리가 영속성컨텍스트에서 변경감지를 통해 자동으로 update쿼리가 나가게 하려면, 원본의 값을 캡쳐해놔야 한다. 그래야 뭐가 변경된건지 알 수 있는거니까.

그래서 메모리가 더 드는거다. 

저렇게 캡쳐를 해 놓은 데이터도 따로 영속성 컨텍스트를 통하여 있어야 하고, 또 그런 변경감지를 위한 로직 또한 실행되어야 한다.

하이버네이트에서 제공하는 readOnly는 이러 한 부분을 생략시켜버리는 것이다.

그래서 최적화가 된다.

 

근데 요새는 GC성능도 좋고 해서 이런 부분들을 readOnly만 할 부분들을 모조리 저렇게 명시해 주며 쿼리힌트를 사용할 필요가 있을까? 하면은..

Redis등을 통하여 최적화를 꾀하는 게 더 좋아보인다.

 

이거는 정말 트래픽이 많은 api 몇개정도만 그냥 넣는거다. 그리고 그 정도면 Redis를 통해 최적화를 꾀하는 게..

 

여튼, 잘 사용하지는 않을 거 같고,

여러가지 join fetch, batch, Redis등으로 최적화를 하고,

저 부분도 성능테스트를 해 보고 효과적일 때 쿼리 힌트를 쓸 것 같다.

 

 

그 다음 Lock

락은 우리가 DB에서 배웠던  Lock 그거다

https://qwefdg3.tistory.com/563

 

간단히 설명하자면,

우리가 이 데이터의 트랜잭션 동안, 다른 세션이 손대지 못하게 하는 것이다.

 

근데 보면 알겠지만 트래픽이 많은 경우는 이거 걸어놓으면.. 좀 그렇다.

https://qwefdg3.tistory.com/558

여기가 LOCK에 대한 설명이 있다.

READ COMMITED가 보통 DB의 기본 설정인 것 같다.

 

여튼

@Lock(LockModeType.PESSIMISTIC_WRITE)
List<Member> findLockByUsername(String usernmae);

이렇게 쓸 수 있고,

PESSIMISTIC_WRITE는 직역은 비관적인 쓰기인데, 말 그대로 쓰기,

이 트랜잭션 동안 다른 트랜잭션의 CUD를 막는다.

 

호출해보면

select
    m1_0.member_id,
    m1_0.age,
    m1_0.team_id,
    m1_0.username 
from
    member m1_0 
where
    m1_0.username=? for update

이렇게 for update 붙어서 나간다.

for update, 저 sql문은 직역은 업데이트를 위한, 이라는 뜻인데,

지금 업데이트 중이니 다른 것은 손 대지마. 라는 의미이다.

 

위에서 말한 PERSSIMISTIC_WRITE와 뜻이 맞다.

 

이 lock은 SpringDataJPA꺼가 아니라 JPA가 지원해 주는 것이다.

 

이번에 배운 것 들은 그렇게 실질적으로 쓸 일이 많지는 않을 것 같다.