You are viewing an old version of this page. View the current version.

Compare with Current View Page History

« Previous Version 3 Next »

JPA는 SQL문을 통해 어플리케이션을 작성했을때보다, 수많은 귀찮은 일을 하지 않아도된다는것을

실습을 통해 파악을 하였습니다. 하지만 실제 그것이 어떠한 SQL문을 수행하는지

또한 그 SQL문이 성능적으로 문제가 없는지 데이터베이스를 병행해서 공부해야하는 과제가 있습니다.

여기서는 다소 자연스럽지 못하더라도 데이터베이스를 공부했을때 일반적인 사항과 JPA의 내용과 엮어보겠습니다.

스캔 실행계획

관계형 데이터베이스의 종류는 많으며, SQL문을 빠르게 작동시키기위해 실행계획을 세우게 되며

이것은 각각의 데이터베이스마다 다르며 풀스캔/인덱스스캔등의 차이에따라도 다른 실행계획이 나옵니다.

또한 IO장치를 어떻게 활용할것인가? 까지 포함하면 더욱더 상이하고 복잡한 실행계획이 수행됩니다. 

SQL문에서 이러한 실행계획을 조정할수 없을뿐더러, 전적으로 DataBase에게 위임하게 됩니다. 

즉, JPA도 이러한 실행계획에 관여할수 없으며 이러한 실행계획을 확인하는 방법은 SQL문을 실제

실행하여 확인하는것입니다.

이름명령어
Oracleset autotrace traceonly
MSSQLSET SHOWPLAN_TEXT_ON
DB2EXPLAIN ALL WITH SNAPSHOT FOR SQL 구문
PostreSQLEXPAIN SQL 구문
MySQLEXPLAIN EXTENDED SQL 구문


JPA TO SQL

JPASQL

studentList = findByGroupInfoName("학생")

SELECT * FROM user ui left join spring.group_info gi

on gi.group_id = ui.group_id and gi.name="학생"

-- 위구문을 어플리케이션사용하기위해서는 실제는

몇가지 더 변환과정이 필요합니다.

dataset = runsql(..)

javaobj = datasetToObj(result)

jsonobj = objToJson(javaobj)


앞장에서 이미 설명된 컨셉이며, JPA가 SQL에 비해 어플리케이션에서 단순하고 일관성있게 사용하는지

보여주는 예입니다. 반환타잎은 Java오브젝트뿐만아니라,웹에서 바로 사용가능한 Json 결과로 뿌려줄수 있기떄문에

JPA에서  사용자정보에서 학생정보만 추출하는 과정이 이 한줄이외에 더 추가되는것은 개발 생산성 낭비로

측정하고 있습니다.  하지만 SQL문을 직접 작성하지 않을뿐 이러한 JPA 인터페이스가 어떤 SQL문으로 변환되어

실행이 되고 실제 데이터베이스에서 실행계획이 어떻게 되는지 점검할 필요가 있습니다.


JPA문이 어떠한 실제 어떠한 SQL문을 실행할지 예측할수도 있지만, 아래 옵션을 켜서 실제 SQL문 확인가능합니다.

  • spring.jpa.properties.hibernate.show_sql=true
  • spring.jpa.properties.hibernate.use_sql_comments=true
  • spring.jpa.properties.hibernate.format_sql=true


어플리케이션의 JPA테스트코드와 SQL 실행을 Trace한 예

쿼리실행을 JPA를 통해 간단하게 할수 있기때문에 , 데이터베이스를 조회하고 변경하는 코드에대해

어플리케이션 레벨에서 TestCase 작성을 쉽게하고 실제 작동되는 SQL문도 체크할수 있습니다.

이것은 유닛테스트 작성에 시간을 줄여,  SQL/SP에는 도저히 코드변경점이 많아 시도해볼수 없는

많을것들을 할수 있게하며, 궁극적으로 어플리케이션이 DB 호출 최적화를 할수 있는 기회를 가질수가 있습니다.

대부분의 성능문제는 쿼리 자체 최적화문제가 아닌, 어플리케이션이 불필요하게  많은 쿼리를 호출하는데 발생합니다.

물론 실행계획까지 개발툴에 통합되어 포함되었으면 하는 바램도 있습니다. 


실행계획 조사하기

번거롭지만, JPA를 학습하고 사용하는동안, JPA사용을 통해 단축된 개발시간을 이러한 습관을 가지는 시간을

확보하는것이  중요해보입니다.


JOIN 작동방식 정리

이것을 정리한 다시 이유는, JOIN및 조건절에 따라 드라이븐이 시작되는 위치가 달라질수도 있고

절차형 프로그래밍에서 이야기하는 루프의 형태가 달라질수 있다란 의미입니다.


대표적인 스캔 타잎은 3가지로 분류할수 있습니다.

  • 이중루프(Nested Loops) : 한쪽테이블을 읽으면서 결합조건에 맞는 레코드를 다른쪽에서 찾음
  • Sort Merge : 결합할 두 테이블을 정렬을 하고 순차적으로 결합
  • Hash : 결합 키값을 해시로 맵핑


이중루프중에도 양쪽풀스캔을(곱하기)하고 랜덤스캔을 하는 케이스가 성능적으로 가장 나쁜 케이스라고 보시면됩니다.


조건이 잘못되거나,필터(파티션)를 고려하지 않거나, 인덱스설정에따라 조인의 실행계획은 최악이 될수가 있습니다.


권한 이양의 죄악

이러한 실행계획 개입은, SQL문을 통해 할수있는게 아니고, RDB에 모든것을 이양해야하고

실제 작동을 시켜 실행계획을 파악해야합니다. 테이블 설계가 동일하다고 가정했을시

코드가 길어도 실행계획이 띄어난 SQL문 VS 간결한 SQL문, 두가지 SQL문이 있다고 하면

전자를 만들수 있는 SQL개발자가 더 뛰어난 개발자로 분류하였습니다.

실행계획이 띄어난 SQL문을 생성하기위해 RDB가 숨기고 있는 내부절차를 깊숙히 파악해야하며

몇가지 일반적으로 최악이 아닌 일반화는 있지만, 표준화가 되기 어려운내용입니다.

JPA의 경우도 이러한 권한을 받을수가 없습니다. 다만  최악의 실행계획을 수행하는 코드를 일반적으로 생성하지 않습니다.

대부분의 경우 JPA의 기본 인테페이스가 이해하는 SQL문이, 우리가 이해하는 SQL문보다 성능적으로 나쁘지 않는 SQL문을 만들어내며

테이블도 자동으로 생성해줄수 있습니다. 하지만 이것만으로 충분하지 않습니다.

JPA 는 SQL문이 하지못하는 몇가지 성능에 관련된 전략을 사용할수가 있습니다.

같은 쿼리라도 달라지는 RDB에의해 변경이되는 복잡한 실행계획에만 의존하지 않겠다는 전략입니다.


Read전략:

  • 즉시읽기( Eager loading) : 
  • 지연읽기 ( Lazy loading ) :

영속성 전이 전략:

  • ALL : 부모의 변화가 자식에게 모두 전가
  • PERSIST : 부모의 영속화 될때 자식도 영속화가됨
  • MERGE : 트랜젝션이 종료되고 변경사항이 merge()수행시 변경사항적용
  • REMOVE : 부모삭제시 연관된 자식도 삭제
  • REFRESH : 부모가 변경되면 자식도 변경
  • DETACH : 부모가 DETACH되면, 연관된 ENTITY도 DEATCH되어 변경반영 죽시 안됨
  • orphanRemoval : 연관관계가 끊어진 자식을 자동으로 제거


위 전략은 모두, 어플리케이션에서 DB와 연관된 불필요한 Read/Write등을 줄이고 그것을 어플리케이션에

반영할수 있는 기회를 제공해줍니다. 이것은 케이스별로 사용해야할 전략이 다르기 때문에

구체적인 예와함께 다시 언급을 하고, 현재는 이러한게 있는것만 알고 넘어가겠습니다. 












  • No labels