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

Compare with Current View Page History

« Previous Version 20 Next »

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

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

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

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

여기서는 다소 자연스럽지 못하더라도

데이터베이스를 공부했을때 성능에 관련된 실행계과 JPA의 내용과 엮어서 내용을 한정짓겠습니다.

실행계획 고려하기

관계형 데이터베이스의 종류는 많으며, SQL문을 빠르게 작동시키기위해 DB엔진은 구문분석후

효과적인 결과를 내기위해 실행계획을 세우게 되며 이것은 각각의 데이터베이스마다  차이가있습니다.

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

JPA에 익숙하다면, 자신이 작성한 함수 인터페이스가, 어떠한 SQL문으로 변환되어 작동이 될지 

예측할수도 있지만, JPA를 믿지 못하겠다고 가정해봅시다.

이럴때 아래옵션을 통해, 함수호출시마다 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마다 다르지만 공통적인 3가지요소가 있습니다.

  • 조작대상 객체:
  • 객체에 대한 조작의 종류
  • 조작 대상이 되는 레코드 수:


순차 풀스캔 VS 인덱스 스캔

엔티티의 설계

@Entity
public class User {
	private String name;

	@ManyToOne
	@JoinColumn(name = "GROUP_ID")
    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;

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

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

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


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

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

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

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

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

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

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



우리가 원하는 집합을 만들기위해, 아래와같은 조인문과 2개의 테이블만 조작하면 좋겠지만

실제는 그렇지 않습니다. 

권한 이양의 죄악

이와같은 실행계획 권한은,SQL문에서 할수 있는게 아니고  RDB에 모든것을 이양해야하고

우리는 단지, 실행계획에 최적화된 SQL문 작성을 위해 노력해야한다는것입니다.

최적화된 실행계획이, 데이터의 변화에 따라 최악으로 변경될수 있다란점도 인지해야하며

최상의 쿼리는, 실행계획이 변하지않는 적당수준의 느린 쿼리문이 될수도 있습니다.

JAP를 통한 튜닝 포인트의 발상변화

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

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

JPA PROXY/영속성 전이등을 사용하여, 어플리케이션 자체에서

DB접근을 최소화하는 전략을 택할수 있으며...


간단하게 설명을하면...

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

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

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

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

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

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

JPA사용이 중복 호출실수를 잡아주는 라이브러리는 아니기때문에

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

Read전략

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

영속성 전이 전략

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


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

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

오히려 SQL/SP 중심적으로 개발했을때 보다, 더 깊이 RDB를 이해하고 학습해야 되지 않나란 생각을 해봅니다.


참고: http://zzong.net/post/15

  • No labels