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

실습을 통해 파악을 하였습니다.

하지만 실제 그것이 어떠한 SQL문을 수행하는지 또한 그 SQL문이 성능적으로 문제가 없는지

데이터베이스를 병행해서 공부해야하는 과제가 있습니다.

실행계획을 예상하고 측정하는것은 아주 광범위한 주제입니다.

데이터베이스를 공부했을때 성능에 관련된 실행계획은 SQL과 더불어 JPA에서도 이해해야하는 항목으로 생각됩니다.

실행계획 고려하기

SQL문을 해석하고 , 원하는 데이터 집합을  IO에서 빠르게 가져오기위한 노력으로

DB엔진은 구문분석후 효과적인 결과를 내기위해 실행계획을 세우게 되며

이것은 각각의 데이터베이스마다  차이가있습니다.

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

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

실행하여 어떻게 작동되는지 확인하는것입니다.

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


JPA Query Method 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="학생"

JPA에 익숙하다면, 자신이 작성한 함수 인터페이스가,

어떠한 SQL문으로 변환되어 작동이 될지  예측할수도 있지만, JPA를 믿지 못하겠다고 가정해봅시다.

이럴때 아래옵션을 통해, 함수호출시마다 SQL문을 확인할수가 있습니다.

이것을 켜는 이유는 JPA가 어떠한 엉뚱한 SQL문을 만들어낼지?

불필요하게 중복호출을 하지 않는지?(사실 이게 가장 중요합니다.)  또는 그 SQL문이

제대로된 실행계획을 가지고 실행이되는지 검증을 하기 위함입니다.


JPA-SQL문 Trace

  • 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문 작성시간을 줄이고, 수많은 케이스를 유닛테스트로 전환될수 있을 가능성을 확인할수 있으며

이것은 데이터중심에서 메시지중심으로의 설계를 가능하게합니다. 


실행계획 조사하기

Trace된 SQL문을 그대로 복사하여, 실행계획을 조사합니다. 매번 이러한 과정을 거칠필요는 없을듯보이며

SQL방식이던 JPA방식이던..., 성능에 의심이가는 부분에대해서는 개발방식과는 별개로 측정되고 분석되어야하는

사항입니다.


실행계획 콘솔모드

실행계획 시각화

참고:DB Management Tool에 따라서 실행계획 시각화기능을 제공하기도합니다.(MSSQL)


실행계획의 출력포맺은 DB마다 다르지만 공통적인 요소가 있습니다.

  • 조작대상 객체
  • 객체에 대한 조작의 종류
  • 조작 대상이 되는 레코드 수
  • 호출 계층


순차 풀스캔 VS 인덱스 스캔

엔티티의 설계

@Entity
public class User {
	private String name;

	@ManyToOne
	@JoinColumn(name = "GROUP_ID",nullable = true)
    private GroupInfo groupInfo;
}
ManyToOne은 JPA에서 관계도 형성을 하는 어노테이션으로서 실제 데이터베이스에서는
아래와같이 테이블생성시 외래키 설정및 인덱스 설정이 자동으로 수행되게 됩니다.
KEY `FKa36i4ekojwk70bxen390i6tek` (`group_id`) USING BTREE,
CONSTRAINT `FKa36i4ekojwk70bxen390i6tek` FOREIGN KEY (`group_id`) REFERENCES `group_info` (`group_id`)

순차풀스캔

JPASQL
findByName("Minsu")SELECT * FROM user where name = 'minsu2';

Name필드에는 아무런 인덱스가 없기때문에, minsu2를 찾기위해 순차적으로

끝까지 조회를 해야합니다. (중복 사용자포함)

데이터량이 많아질시 탐색시간이 같이 선형적으로 증가합니다.


인덱스 스캔

JPASQL
findByGroupId(1)SELECT * FROM user where group_id = 2;

인덱스가 설정된 필드는, 이미 특정 탐색 알고리즘을 위해 데이터 분류가 되어 있습니다.

이경우 모두 순차 비교검사 할필요는 없이, 탐색 회수를 줄일수 있습니다.

적은량의 데이터에서 탐색시 효과가 없을수 있으나,  데이터량이 늘어남에따라 효과를볼수가 있습니다.

인덱스가 걸려있다고 항상 인덱스 스캔이 되는것은 아닙니다.

원하는 데이터를 뽑기위해 SQL문은 더 복잡해질수 있으며 실행계획은 DBMS가 세우며

옵티마이져 통계에의해  어떠한 테이블에서 풀스캔이 더 효율성이 좋다고 판단되면

인덱스를 무시하고 풀스캔이 진행될수도 있습니다. 이것은 실행계획을 조사하면

나오는 Tip으로 (Mysql은 Extra) 알수가 있습니다.


인덱스 최상위 Type은 페이지 물리적 재배열여부에따라 클러스터와 논클러스터로 나뉠수 있으며

동일 데이터라고 해도 스캔의 횟수가 달라질수 있습니다.

클러스터 인덱스 VS 논클러스터 인덱스
비교클러스터 인덱스논클러스터 인덱스
차이물리적으로 행을 재배열물리적으로 재배열 하지않음
스캔 방식

페이지크기작음
선택도
  • 30% 이내에
  • 키값이 이미 페이지단위로 정렬이 된상태이기때문에 7,8,9를 찾기위해 IO접근 1번발생
  • 3%이내
  • 7,8,9를 찾기위해 페이지 2,3를 두번 접근하는 IO 발생



페이지 논리갯수테이블당1테이블당 249

지정방법

(DB마다 다를수있음)

PRIMARY KEY에서만 사용될수 있습니다.

정정: 테이블당 유니크한키로 한번만 지정가능합니다. PRIMARY KEY에 걸려버렸다고

치면...이후 다른키에 또 걸수 없습니다.


( 기본이냐 옵션이냐의 차이는 DB마다다름)

명시해서 지정
키의이점

데이터 값의 범위가 크며, 키에따른 Order규칙이

키가 조인에 자주사용됨


장단점
  • 조회수가 수정수보다 월등히 많을때
  • 빈번한 업데이트에서 재인덱싱이 일어나기때문에 분리
  • 데이터 추가에따른 인덱싱페이지의 구조변화가 없기때문에 데이터 추가에 유리
  • 데이터량이 많아짐에따라 인덱스페이지가 파편화되기때문에 검색은 불리해질수 있음








선택도와 실행계획의 분리 추가 참조사이트

스캔 범위/방식에따른 실행계획 전략

성능을 위한 스캔 범위 Type은 3가지정도로 요약할수 있습니다.

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


어느것이 항상빠르며,실행계획이 고정이된다라고 볼수 없으나

Sort Merge가 되는방식이 일반적으로 성능에서 안정적입니다.(편차가 크게 없이 빠름)


스캔범위및 실행 계획이 복잡해지지 않게 하기 위한 가장 간단한 규칙이지만,

우리가 원하는 집합을 만들다보면 이 규칙을 깨트리기 쉽고, 쿼리능력자에따라 차이가 날수있습니다.

  • 스캔 범위 줄이기
  • 조인을 덜 복잡하게 만들기

참고 : https://community.modeanalytics.com/sql/tutorial/sql-performance-tuning/



우리가 원하는 집합을 만들기위해, 2개의 테이블만 조작하여 JOIN문을 사용하면 좋겠지만

실제는 그렇지 않습니다. 

JPA 튜닝 포인트

JPA에서는 실행계획이 틀어지고, 예측하기 어려운 튜닝포인트를 쿼리가 아닌

OOP중심, 즉 어플리케이션서 관여할수 있는 계층을 하나더 추가하였습니다.


JPA에서 이루어지는 성능에 관련된 기능을 간단하게 설명을하면...

User user1 = findByName("Minsu");
User user2 = findByName("Minsu");
user1.GetName()
user2.GetName()

위와같은 코드는 일반적인 SQL/SP 호출 어플케이션에서는, 두번의 Select문을 DB에게 요청을 하였을것입니다.

JPA에서는 , 동일 트랙잰션에서 위와같이 동일처리라고 판단되는 사항에대해

SELECT SQL한번만 조회를하고, DB 호출없이 오브젝트 재사용전략을 택합니다.

DB에서는 상황에따라 옵티마이져가 불필요한 IO를 접근하지 않고, 메모리를 재활용할수 있는 상황입니다.

JPA 어플리케이션 레이아웃에서는, SQL호출 자체도 하지 않겠다는 의미입니다.

SQL호출을 하지 않는다란 말은 다음과정이 내포되어있기때문에 호출횟수에따라 아주 큰 비용이 될수도 있습니다.

SQL문요청 → 네트워크 전송 → DB엔진이 구문분석 → IO Read → 네트워크로 결과 반환 

JPA의 이러한 캐시처리같은 메카니즘이 사용 중복 호출실수를 잡으려고 이렇게 작동되는것은 아니며

어플케이션 레이아웃에서 관여할수있는 성능에 관련된 몇가지 JPA컨셉을  추가로 학습을 해야합니다.

매번 SQL문을 호출하고 즉시 답을 얻는 방식과 달리, 아래와같은 몇가지 컨셉이 존재합니다.


Read전략

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

영속성 전이 전략

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


JPA는 객체지향과 데이터모델링 사이에 간극을 최소화하고 편리한 방식임에는 분명하지만

개발 난이도를 줄였다라고 판단할수는 없을듯 합니다. 

오히려 SQL/SP 중심적으로 개발했을때는 신경쓰지 못한 부분에대해

더 깊이 RDB를 이해하고 JPA의 추가적인 영속성 특성을같이 잘 활용해야 사용측면에서도

성능 측면에서도 실무에 적용이 될것으로 기대해봅니다.

이 부분은 개념과함께 구체적인 사용사례가 필요한 부분이며, 샘플을 좀더 준비할예정입니다.



JPA에서 작성한 인터페이스가 쿼리를 직접 호출합니다. 문제는 기존 접근정책에서 어플리케이션에 Select권한을 허용하지 않으며

SP를통해서만 개발이 진행되어야 하는 정책이 아직도 있다란것에서 출발합니다.


정부가 프레임워크 표준을 정하여 고정하는것도 우스운일이지만(액티브X의 실패와마찮가지로)

Spring은 전자정부 표준 웹 프레임워크입니다. 이와는 별개로 자바에서는 DB를 제어하는 표준 ORM기술로 JPA를 채택하였습니다.

그리고 Spring역시 JPA를 적극적 통합및 지원을 하고.., SQLMapper라 불리는 Mybatis 최신버젼 공식지원 명단 제외하였습니다.(Mybatis가 Spring에 맞춰야한 상황이됨)

  • 정부 웹표준 = Spring
  • Spring의 웹요소 DB 접근 표준 = JPA, 바티스 안녕
  • 최신 Spring을 추구하면서 바티스를 버리지 못함...바티스가 안되는 스프링은 버릴수 있음 ( 그럼 스프링의 의미는?)

위와같은 모순이 생긴다란점입니다.


기존 개발된거야 바꾸기 어렵다 하더라도 최신 Spring버젼을 선택하면서 Mybatis조합을 하면서

기존개발방식을 개선하고 최신트렌드로 가고 있다라고 이야기하는것입니다.

Mybatis가 나쁘다란 말이 아닙니다. Mybatis는 기존 웹-DB 개발방식을 고수하는 쪽에 가깝습니다.



SpringAsp.net
SQL/SP 매핑MybatisEnterprise Library
ORMJPAEntity framework
LanguageJAVAC#

DB 개발방식 개선의 이슈로 Asp.net → Spring으로 변경한다라고 가정해봅시다.

웹 프레임워크 내에서 DB를 제어하는 핵심 라이브러리가 무슨 특징을가지고 있는지? 위 도표를 이해하고 구분하는 개발자를 만나기는 어려웠습니다.

그리고 닷넷에서 ASP.net개발자가 기본으로 MS가 선물로주는 DB라이브러리가 무엇인지? 알고있고 구분해서 사용하는 개발자분도 만나기 어려웠습니다.

닷넷을하던 자바를하던.., 자신이 사용하는 기술이 상대편에서는 어떠한 형태로 사용되어지는지 파악하는것은 아주 중요한 요소라고 생각합니다.

Mybatis가 ORM이다라고 하시는분도 있으며, JPA는 쿼리를 모르는 개발자가 쉽게개발하기위해 성능을 올릴수 없는 저급한 플랫폼이다라고 결론짓는 분도 보았습니다.

또한 심각한것은 MS를 사용하는 진영은 SP로만 개발해야된다란 믿음에서 벗어나지 못한다란것입니다.

자바 오픈진영에서 ORM은 여러기술이 경쟁을 펼치고 몇개로 표준이 되는반면 MS에서는 애시당초 ORM 기술을 하나로만 진행하고 표준화하였으며 , Spring에 JPA를 셋팅하는과정보다 훨씬 쉽습니다.


위 도표를 그린 이유는, ORM을 여러가지 이유로 적용할 준비가 안되었다라고 하면

Enterprise Library VS Mybatis 의 차이점을 심도있게 검토할수 있습니다.


주위에 두가지 위 도표를 이해하고 차이에대해 고민을 못하였다고하면  SP개발 VS 쿼리개발방식만 이야기할수 있습니다.

아직 우린 ORM으로 가기위한 준비를 더 할 필요가 있겠습니다.


다시 MS개발진영으로 문제범위를 좁혀보겠습니다.

MS를 사용한 개발 진영은, SP를 통한 개발방식이 권장이 아니라 정책적 타협 불가한 요소인곳도 있을수 있으며

이것이 MS권장방식이라고 믿고 계신분들도 있습니다. 아주 오랜전 결정된 정책( 그때 정책을 만든선배들은 이미없음) 에 대한 이해및 히스토리가

대부분 없는 경우도 많으며, 현재 고착화된 DB개발 정책을 약간이라도 변경하는것은 큰모험에 가까울수 있습니다.


우선 SP를 통한 개발방식의 절대적인 장점 그러한 굳은 믿음 ,그것을 타파하는것에 시작을 해보겠습니다.

MS는 JAVA오픈진영보다 훨씬더 빠르게 ORM을 통한 개발 요소를 프레임워크로 표준화하고 통합하였습니다. -Entity FrameWork

어플리케이션 개발자가 ORM적용을 주도하려면 DB개발지식을 가지고 토론해야하기때문에 불편해지는 상황이 생길수 있습니다.


다음은 ORM 사용시 오해받는 몇가지 주제입니다.

  • ORM의 쿼리는 통제가 안되고 느릴것이다. ==> TypeSafet한 방식의 쿼리빌드를 선택하고,표준쿼리를통해 성능문제를 해결하려고 합니다.
  • ORM에서는 쿼리 최적화가 불가 ==> 표준화되지 않는 각종 DB의 네이티브 쿼리의 방언을 이해하고 있습니다. 심지어 SQL2000버젼부터 2018년 버젼까지 다른점도
  • ORM은 쿼리 개발을 모르는 초짜가 사용하는 기술이다. ==> QueryDSL은 SQL을 이해 못하면 람다식으로 SQL식을 풀어낼수 없으며, 쿼리작성보다 엔티티를 먼저 맞추는데 시간을 들입니다. 그리고 쿼리식과 람다식은 닯아있습니다. ORM에서 오히려 순차적 중복 쿼리 호출을 방지하는 장치가 있습니다. SP내부에서 SQL문이 오히려 여러가지 순차 SQL문을 만들가능성이 있습니다. ( 선언적 개발에 대해 이해가 필요할수 있습니다. )
  • ORM은 ERD를 그려낼지 모르는 개발자가 사용하는 기술이다 - ==> 기존개발방식이 ERD설계된 관계를모르고, 조인남발 쿼리로 잘못된 집합체를통한 뷰어개발이 가능합니다. 그리고 대부분 ERD설계가 존재하지 않습니다 , ORM에서 ERD관계를 맞추지못하면 빌드 조차 안되는 경우가많습니다. 그렇기때문에 ERD 설계도가 없다고치면 그관계 파악이 우선입니다.
외래키로 널허용이 금지된 1:1관계를 파악하고, JPA는 왜 자동으로 Inner Join을 걸게되는가?
JPA를 설계한 쿼리가 일반 쿼리개발자보다 일반적으로 똑똑하다란 것입니다.
대부분 쿼리로 개발한 개발자는 널허용여부에따라 왜 InnerJoin을 걸어야하는가에 대한 답을 빠르게 하지 못하지만
JPA는 놀랍게 그러한 표준적인 쿼리성능 빌드에대해 고민을 하였으며 외래키에 Null허용을 할경우 놀랍게도 InnerJoin을 사용하지 않았습니다. 
물론 실행계획을 반영하여 극단적인 쿼리튜닝이 필요해 DBMS가 제공하는 네이티브 쿼리가 필요한 경우 ORM은 도움되지 않습니다.
성능의 문제를 쿼리가 오브젝트를 관리하는 메시지중심으로 풀어갈수 있는 방법을 JPA가 제공해주지만 RDB와 어플리케이션이 가진 객체를
둘다 이해해야하는것으로 ERD와 UML의 불일치를 해결을하고 , 그것을 일치하여 성능문제를 해결하려는 시도는 
쿼리를 극단적으로 튜닝해야하는 주제처럼 둘다 어려운주제로..., JPA가 모든상황에 성능이 항상떨어진다라는것은 올바르지 않습니다.

DBMS및 SQL버젼마다 다르게 사용되고, 그속에서도 각 개발자마다 다르게 사용되는 네이티브 SQL문은 골칫거리입니다.

DBMS 변경의 이슈가 아니라, 대부분 동일 DB에서 버젼을 올리는것조차 포기한것을 많이 봐왔습니다.

ORM에서는 DB및 버젼마다 SQL문이 달라지는 케이스를, 특정 회사 DB개발자를 통틀어합한것보다 더 잘이해하고 있으며


특수한 쿼리를통한 성능 최적화 VS 가급적 ORM 인터페이스 사용

뭐가 득이고 실인지? 잘 판단해야 하는 이슈입니다.




DB용어를 사용하여 조급더 고급지게 접근해보겠습니다.

  • DDL(Data Definitison Language)의 테이블 객체의 생성(Create),변경(Alert),삭제(Drop)등을 수행
  • DML(Data Manipulation Language) : 스키마객체의 데이터를 Insert/Update/Select등을 할수 있는 명령어(표준화되어있어서 거의 공통)
  • DCL(Data Control Language) : Commit,RollBack,SavePoint등을 할수 있는 명령어(DB종속적인 경우가 많음)

JPA-ORM은 기본적으로 DDL/DML/DCL을 모두 할수 있는 구조입니다.

이것은 ORM이해가 아닌 기존 RDB가가진 언어입니다. 접근 정책정의에 있어서 중요요소 3가지를

빠트리고 이야기가 진행되는경우가 많지만... 정책결정은 위 3가지를 포함해서 이야기되어야하는 항목으로 생각됩니다.

그리고 각각 형상관리와 배포계획은 어떻게 세울것인가란 복잡한 이야기로 연결될수 있으나, 우선 배포는 배제하겠습니다.


SP를 이용한 장점:

  • SP를 통한 통제방식은 MSSQL과 상관없이 , DB접근 정책을 정하기 아주 단순/명쾌 해지기 때문입니다.
  • 또한 쿼리품질을 SP를 통해 제어하고, 비지니스로직을 집합시키는데 용이합니다.
  • 결국 DDL/DML/DCL 어플리케이션별로 세부적인 접근 정책이 필요없어지게 됩니다.
  • 그리고 쿼리최적화를 통한 SP호출방식이 실행계획 성능 향상에 일반적으로 도움이 된다.


ORM을 이용한 장점:

  • 고성능 분산처리 어플리케이션 설계를 위해서는 DML 특히 트랜잭션에 관련된 DCL이 가능해야함 ( Entity의 영속성및 Proxy개념 )
  • SP내부에 작성되는 쿼리는 일괄적인 품질관리가 안되며, SP자체가 형상관리가 안되고 결국 SP는 이야기합니다. 나는 안쓰인다 고로 존재한다.
  • 고성능 분산처리를 위해 트랜잭션에 어플리케이션이 안정적이고 더 효율적이게 관여할수 있는 개발방법이 많이 등장하였습니다.
  • 인덱스를 걸고 실행계획을 안정적으로 유지하는것은 DBMS마다 약간의 차이가있고 그리고 그것은 비표준 SQL문 작성을 강요하며 일괄적으로 유지가 어렵습니다. 그래서 성능이슈를 ORM은 다른방식으로 풀어나갑니다.


SP를 통한 DB개발에서 이야기하는 장점이 오늘날에는 단점이 된 케이스가 일부있고

서로 상반된 이야기를 하기때문에 풀어나가기가 어려운 주제임은 분명합니다.

정답은 어렵고 권한이 없는이상 이것을 힘들게 주도하여 바꾸는것을 권장하지 않습니다.

언젠가 쓰일수 있으며, 왜 ORM은 우리회사에 적용이 어려운가란 주제는 RDB를 공부하는데 아주 좋은 주제가 될듯 합니다.







  • No labels

2 Comments

  1. Anonymous

    I might be beniatg a dead horse, but thank you for posting this!
  2. Anonymous

    I'll try to put this to good use imlmeiatedy.