RxJava는 Reactive java에서 이름을 따왔다.


Reactive programming(리액티브 프로그래밍) 패러다임을 자바에서 구현한 프로그래밍 라이브러리이다.


프로그래밍 패러다임에는 여러가지가 있는데 OOP(객체지향), Function(함수), Imperative(명령형) 등이 있다.


대체로 많은 프로그램들이 명령형 프로그래밍이라고 할 수 있고 여기에는 자바, 파이썬, C, Node.js등도 포함 되어 있다.

특정 언어라고 해서 한가지 프로그래밍 패러다임만 사용하는건 아니지만 특정 목적에 맞게 설계된 언어들이 있다.


여기에서 '패러다임(paradigm)'은 방법론 정도로 보면 된다.


여러가지 언어 중에서 자바는 OOP(Object Oriented Programming)라고 해서 객체지향형 프로그래밍의 대표 언어이고 Functional Programming(펑셔널 프로그래밍)을 대표하는건 파이썬, node.js등이라고 할 수 있다. 자바로 Reactive Programming을 해야할 일이 생겨서 이 라이브러리가 등장 하였다. 그리고 자바가 버젼이 올라가면서 여러가지 요즘 트렌드에 맞게 기술들이 추가 되어서 OOP기반이었던 자바가 Functional, Reactive 등의 프로그래밍 방법론으로도 개발이 가능하게 되었다.


리액티브란 외부에서 자극이 오고 그에 대해 반응 한다는 뜻이다. 


아래는 rxJava로 hello를 출력한 코드이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
import io.reactivex.Observable;
 
public class FirstExample {
    public void emit(){
        Observable.just("hello""rxjava2!!")
            .subscribe(System.out::println);
    }
    public static void main(String[] args) {
        FirstExample firstExample = new FirstExample();
        firstExample.emit();
    }
}
 
cs

코드를 잠깐 살펴보면 Observable와 .just()가 나오고 .subscribe()가 나오고 그 안으로 System.out::println이 들어가는 구조를 볼 수 있다.


Observer라는 것을 사용하는게 RxJava이다.


Function

Function은 쉽게 이야기 해서 제네릭으로 <기존타입, 리턴타입>을 받아서 .apply()를 하면 기존 타입의 연산 결과를 결과 타입으로 반환을 해준다.

소스코드는 아래와 같다.


1
2
3
4
5
6
7
8
9
10
import java.util.function.Function;
 
public class FunctionExample {
    public static void main(String[] args) {
        Function<String, Integer> function = str -> Integer.parseInt(str);
        Integer integer = function.apply("10");
        System.out.println(integer);
    }
}
 
cs


gradle로 빌드한 소스코드는 아래 repository에 올려 놓았다.

https://github.com/Kyeongrok/rxjava_helloworld/



Consumer(컨슈머)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Observable<Integer> source = Observable.create((ObservableEmitter<Integer> emitter) -> {
    emitter.onNext(100);
    emitter.onNext(200);
    emitter.onNext(300);
    emitter.onComplete();
 
});
 
// 람다 + 메소드 레퍼런스
source.subscribe(System.out::println);
 
// 그냥 코드
source.subscribe(new Consumer<Integer>() {
    @Override
    public void accept(Integer integer) throws Exception {
        System.out.println("result : " + integer);
    }
});
cs

컨슈머는 값을 받는 익명 void함수다.


위 코드 10번줄 처럼 한줄이면 끝나는 코드를 그냥 쓸려면 13~18 이렇게 길게 써야 한다.

'프로그래밍 > JAVA ' 카테고리의 다른 글

java paging class (자바 페이징 클래스)  (0) 2016.08.19
블로그 이미지

낭만가을

,

테이블을 저장하는 공간이란 의미도 틀린것은 아니지만 정확한 의미는 아닙니다.

 

오라클은 데이터베이스 관리 시스템이고 말 그대로 데이터들을 관리합니다. 즉 어딘가에 데이터들을 저장, 추출, 삭제, 변경하는 작업을 할 수 있는 것입니다. 그렇다면 데이터는 어디에 저장되는 것일까요? 물론 파일에 저장됩니다.

 

오라클 데이터베이스는 데이터 파일들을 가지고 있으며, 이 파일들에 데이터가 저장됩니다. 그런데 파일은 데이터가 저장되는 물리적인 공간을 말하는 것입니다.

 



 

 

오라클 내부에서는 데이터 블록(data block), 익스텐트(extent), 세그먼트(segment), 테이블스페이스(tablespace)라는 논리적인 개념으로 데이터 들을 관리합니다.

 

오라클에서 데이터를 저장하는 가장 최소의 논리적인 단위가 데이터 블록이며, 데이터 블록이 모여서 익스텐트가 되고, 익스텐트가 모여서 세그먼트, 세그먼트가 모여서 테이블 스페이스가 됩니다. 실제로 물리적인 데이터 파일(확장자가 dbf 혹은 ora인 파일)은 테이블 스페이스와 대응됩니다.

 

하나의 테이블 스페이스는 최소 1개의 데이터파일로 구성됩니다.

 

테이블 이외에도 인덱스, 프로시저, 뷰 등 다른 여러 가지 오라클 객체들이 저장됩니다. 

블로그 이미지

낭만가을

,

SQL 튜닝은 "SQL + 튜닝"입니다. 즉, SQL 튜닝이란 튜닝 대상이 되는 SQL을 이해하고, SQL이 가진 정보(테이블/인덱스/컬럼의 정보 및 업무적 성격 등)를 치밀하게 분석하여 얻어지는 결과라고 생각합니다. SQL 튜닝을 시작하기 위해서는 SQL에 대한 이해가 선행되어야 한다고 생각합니다. 왜냐하면, SQL의 작성형태에 따라 다양한 성능 문제가 발생되기 때문입니다.

 

SQL 튜닝의 시작은 SQL의 의미(작성 의도)를 제대로 파악하는 것입니다. SQL의 의미를 정확히 파악하지 못한다면, 원본 SQL에서 추출하고자 했던 결과 집합이 아닌 다른 집합을 추출하게 될지도 모릅니다. 이러한 개선안은 개선안이라고 할 수 없습니다. 즉, 원본 SQL의 작성 의도를 제대로 파악하지 않고, 단순히 I/O 발생량을 줄인다거나 수행시간을 단축하기 위해 힌트를 남발하는 것은 상당히 위험한 일입니다. 그러므로 SQL의 의미를 파악하는 것이 성능 문제를 유발하는 SQL에 대한 개선의 시작이라 말할 수 있습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
SELECT *
FROM  (
  SELECT /*+ INDEX_DESC(A IDX_MOX_SENDDATE) */
    a.*,
    ROWNUM as rnum
  FROM   tbs_mbox a
  WHERE userid = :b1
    AND status = :b2
    AND ROWNUM <= :b3
  )
WHERE rnum >= :b4;
 
SELECT STATEMENT - CHOOSE- COST ESTIMATE:3
VIEW
  COUNT STOPKEY
  TABLE ACCESS BY INDEX ROWID :imsi.tbs_mbox
  INDEX RANGE SCAN DESCENDING :imsi.idx_mbox_senddate

 

해당 SQL은 메일 프로그램에서 사용되며, 동시 간대(특히, 업무 시작 시간, 09:00)에 다수의 다용자가 사용하는 특성이 있습니다. 뿐만 아니라, 데이터를 추출하는 과정에서 발생하는 경합으로 인해 DB 서버 전반적인 성능 저하를 유발하는 악성 SQL입니다.

 

해당 SQL은 INDEX_DESC 힌트와 ROWNUM 조건으로 인해 COUNT STOPKEY의 실행계획이 수립되었습니다. 따라서 전체 데이터를 모두 처리하지 않고, RONUM, 조건에 해당하는 일부의 데이터만 처리할 것으로 예상됩니다. 즉, 실행계획 상으로는 SQL의 문제점을 찾기가 쉽지 않습니다. 따라서 인덱스 구성과 힌트 사용이 적절한지에 대한 검토가 필요합니다.

 

INDEX_NAME          COLUMN LIST
-----------------   -----------------
idx_mbox_status     userid, status
idx_mbox_senddate   userid, senddate

 

인덱스 구성만 보면, 해당 SQL은 IDX_MBOX_SENDDATE 인덱스보다는 USER_ID, STATUS 컬럼으로 구성된 IDX_MBOX_STATUS 인덱스를 사용하는 것이 유리해 보입니다.

 

그런데 조건 절에 적합한 인덱스 구성만 확인하고, 해당 SQL의 의미를 파악하지 않은 채 성능 개선을 하려고 한다면, 잘못된 개선안을 도출할 가능성이 높습니다.

 

INDEX_DESC 힌트의 인덱스 명만을 변경하는 것으로 결론을 도출한다면, 아주 치명적인 실수를 저지를 수 있습니다. 왜냐하면, SQL의 작성 의도와는 전혀 다른 데이터가 추출될 수도 있기 때문입니다. 물론, 경우에 따라 USERID, STATUS로 구성된 인덱스를 이용하는 것이 올바른 방법일 수도 있습니다. 그러나 이와 같은 개선안은 "USER_ID, SENDDATE 컬럼으로 정렬이 필요없다"는 전제 조건을 반족해야 합니다. 이러한 업무적인 내용은 개발 담당자 또는 업무 운영자에게 문의할 필요가 있으며, 그렇지 못한 경우라면 SQL 자체에서 작성 의도를 찾아봐야 합니다. 그렇다면, 해당 SQL의 작성 의도를 파악해보겠습니다.

 

- ROWNUM을 사용하였으므로 PAGINATION QUERY일 가능성이 높다.

- INDEX_DESC 힌트는 ORDER BY DESC를 대체하려는 것으로 보인다.

- 인덱스 컬럼 정보와 힌트를 고려해 보았을 때, 의미사응로는 "ORDER BY USER_ID DESC, SEND_DATE DESC"가 내포된 것으로 보인다.

 

앞에서 파악한 SQL의 의미를 통해, 추출하고자 하는 데이ㅏ터는 조건에 만족하는 "최근 발송된" ROWNUM <= :B3개의 메일 중에서 RNUM >= :B4인 메일이라는 것을 알 수 있습니다. 즉, INDEX_DESC 힌트에 내포된 의미를 풀어보면 다음과 같습니다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
SELECT x.*
FROM (
  SELECT a.*,
    ROWNUM AS RNUM
  FROM (
    SELECT a.*
    FROM tbs_mbox a
    WHERE userid = :b1
      AND status = :b2
    ORDER BY a.userid DESC,
      a.senddate DESC
        ) a
  WHERE ROWNUM <= :b3
     ) x
WHERE
  x.rnum >= :b4

 

재작성한 SQL을 보면, 원본 SQL에서 INDEX_DESC 힌트의 인덱스 명만을 변경하는 것은 올바른 튜닝 방법이 아니라는 것을 알 수 있습니다. 왜냐하면, INDEX_DESC 힌트에 사용된 IDX_MBOX_SENDDATE 인덱스는 조건에 대한 처리뿐만 아니라 ORDER BY DESC  역할도 수행하기 때문입니다. 따라서 SQL의 의미를 파악한 후에 도출될 수 있는 개선안 중의 하나는 IDX_MBOX_SENDDATE 인덱스에 STATUS 컬럼을 추가하여 재 생성하는 것입니다. 이와 같이 SQL 튜닝을 위해서 가장 필요한 것은 SQL을 정확하게 이해하는 것입니다. SQL의 의미를 파악해야만 올바른 SQL 개선안이 도출될 뿐만 아니라, 때로는 SQL의 의미 파악 자체가 SQL 개선으로 이어지는 경우도 있기 때문입니다.

 

개발자나 DBA 들 중의 일부는 SQL 튜닝의 세계에 뛰어드는 것을 매우 어려워합니다. 왜냐하면, "SQL 튜닝"이란 용어 자체가 친근하지 않을 뿐 아니라 SQL 튜닝의 시작점을 정하기도 쉽지 않기 때문입니다. SQL의 의미를 해석하는 것으로부터, 이미 SQL 튜닝이 시작됩니다. 


블로그 이미지

낭만가을

,

쿼리 튜닝은 온라인 SQL이냐 대용량 배치 SQL이냐에 따라 튜닝방법이 달라집니다. 하지만 대용량 배치는 프로그램 수가 많지 않은 편입니다. 온라인 SQL 튜닝에서도 관점에 따라 튜닝방법이 다르게 됩니다. 예를 들어 Peak Time에 Insert 문이나 Update 문, Select 문이 집중적으로 몰릴 때의 튜닝 방법이 있고, 단순히 SQL 하나에 집중해서 응답시간을 최소화하는 튜닝방법이 있습니다. 본 포스팅은 일반적으로 가장 많은 튜닝 사례에 해당하는 Select문 튜닝방법론을 기술한 포스팅입니다. 학습 용도로 작성한 포스팅으로 본 포스팅의 원본 출처는 Science of Database 블로그 SQL 튜닝방법론 입니다.




온라인 Select문 튜닝 방법론

온라인 SQL의 튜닝방법은 여러 가지가 있을 수 있습니다. 하지만 그 중에서 가장 기초적이고, 기본적인 방법입니다. 아래의 7가지 항목을 점검하고 약한 곳을 보강하면 됩니다. 이글은 SQL 튜닝책을 두 권정도 본 사람들을 위한 것입니다. 튜닝에 자신있는 사람들은 볼 필요가 없습니다.


1. 적절한 인덱스를 사용하여 Block I/O를 최소화 하라

조인이 없는 경우는 적절한 인덱스를 사용하는 것만으로도 상당한 효과를 볼 수 있습니다. 조인이 있는 경우는 특히 Driving(선행) 집합에 신경을 써야합니다. 왜냐하면 Nested Loop 조인을 사용했고, 선행집합의 건수가 많다면, 후행집합의 조인의 시도횟수가 증가하므로 성능이 느려지게 됩니다. 따라서 적절한 인덱스를 이용하여 선행집합의 건수를 줄인다면, 혹은 가장 적은 집합을 선행으로 놓는다면, 후행집합으로의 조인건수는 줄어들게 됩니다. 물론 이때에도 후행집합의 적절한 인덱스는 필수 조건입니다. Driving 집합의 Block I/O를 줄이기 위하여 최적화된 인덱스가 없다면 생성하고, 있다면 그것을 사용하십시오. 다시 말해 최적의 Access Path를 만드는 것이 좋습니다.  


운영중인 시스템이라면 최적의 Access Path를 위해 인덱스를 변경하거나 생성할 때는 주의해야 합니다. 현재 튜닝하고 있는 SQL에 최적화된 인덱스를 생성하더라도 다른 SQL에 악영향을 줄 수 있기 때문입니다. 인덱스를 생성하거나 변경할 때는 그 테이블을 사용하는 다른 SQL의 실행계획이 변경되지 않는지 각별히 신경써야 합니다. 이런 이유 때문에 개발과정에서 효율적인 인덱스 설계가 중요시 됩니다.


2. 조인방법과 조인순서를 최적화 하라.

온라인에서 사용하는 Select문은 좁은 범위를 검색하는 경우가 많습니다. 이럴때는 대부분 Nested Loop Join이 유리합니다. 그러므로 조인건수가 소량인 SQL에 Hash Join이나 Sort Merge Join이 발견되면 Nested Loop Join으로 변경하는 것이 더 유리한지 검토해야 합니다. 물론 여기서도 Nested Loop 조인에 관해서만 다룹니다.


Nested Loop 조인에서 가장 중요한 것은 조인 순서입니다. From절에 테이블(집합)이 두 개라면 후행집합의 관점에서는 적절한 인덱스만 존재한다면 그것으로 족합니다. 만약 From절에 테이블(집합)이 세 개 이상이라면 조인순서를 변경할 수 있는지에 대한 두 가지 원리를 사용하면 좋습니다. 두 가지 원리는 아래의 단락에서 소개됩니다. 아무리 조인할 집합이 많다고 하더라도 이 두 가지의 원리는 동일하게 적용될 수 있습니다. 두 가지 원리를 이용할 때 필요하다면 Leading 힌트를 사용해야 합니다.


첫번째, 후행집합에 적절한 인덱스가 없는 경우에 조인순서를 바꾸면, 최적의 인덱스를 사용할 수 있는 경우가 많습니다. 예컨대, 튜닝전의 조인순서가 A → B → C라고 하면, 중간 집합인 B에 적절한 인덱스가 없고 오히려 C에 적절한 인덱스가 존재하는 경우가 있습니다. 이럴 때는 B에 인덱스를 무작정 생성하지 말고, 조인순서를 A → C → B로 바꿀 수 있는지, 바꾸는 것이 더 효율적인지 검증하면 좋습니다. 조인 순서만 바꿔주어도 일량이 획기적으로 줄어드는 경우가 많습니다. 만약 조인순서를 바꿀 수 없거나, C를 중간집합으로 하는 것이 비효율적이라면, B를 중간집합으로 유지하고 적절한 인덱스를 사용해야 합니다.


두번째, 조인되는 집합 중 특정 인덱스에서 Block I/O가 증가하는 경우에 조인순서의 변경을 검토하면 됩니다. 이때 10046 Trace나 DBMS_XPLAN.Display_Corsor를 이용하면 조인집합들의 Block I/O량을 관찰할 수 있습니다. 예를 들어, 튜닝전에 조인순서가 A → B → C라고 하고, 집합 B에서 Block I/O량이 증가하면 A → C → B로 바꾸면 일량이 줄어드는 경우가 많습니다. C를 먼저 조인(Filter)하여 선행집합(B의 입장에서는 C가 선행이다)의 건수를 줄이고 B에 조인하면 성능이 향상됩니다.


3. Table Access(Random Access)를 최소화하라.

Random Access란 rowid로 테이블을 액세스하는 것을 말합니다. 1번과 2번을 최적화했다면 Random Access도 자동으로 많이 줄어들었을 것입니다. 하지만 그것이 끝은 아닙니다. 여전히 성능이 만족스럽지 못한다면 Random Access 횟수를 줄이는 것을 간과해서는 안됩니다.


인덱스를 사용하면 rowid가 자동으로 획득됩니다. 만약 인덱스에 없는 칼럼을 Select 해야 한다면 rowid로 테이블을 액세스 해야합니다. 이때 테이블로액세스해야 할 건수가 많고, 인덱스 컬럼순으로 테이블이 sort되어 있지 않다면 성능이 매우 저하됩니다. 왜냐하면 테이블이 인덱스 기준으로 sort되어 있지 않기 때문에 테이블을 방문할 때마다 서로 다른 블럭을 읽어야 하기 때문입니다.


비유적으로 설명해보겠습니다. 우리가 심부름을 할 때 세 군대의 상점(A, B, C)을 들러야 한다고 치겠습니다. 그 상점들이 모두 한 건물 내부에 존재한다면 얼마나 좋겠습니까? 그 심부름은 매우 빠른 시간에 끝날 것입니다. 하지만 반대로 상점 A는 부산에 있고 상점 B는 대구에 있고, 상점 C는 서울에 있다면? 만약 당신의 성격이 매우 좋아서 그 심부름을 한다고 해도 시간이 많이 걸릴 것입니다. Random Access도 마찬가지 입니다. 인덱스의 rowid로 테이블을 방문할 때 테이블이 인덱스기준으로 sort되어 상점처럼 다닥다닥 붙어있다면 성능은 매우 빠르고, 흩어져 있을수록 성능은 느려집니다. (오라클에서는 테이블이 인덱스 기준으로 sort되어 있는 정도를 Clustering Factor라고 합니다) 바로 이런 이유 때문에 index scan 보다는 Table Scan이 느린 것입니다. 따라서 우리는 Random Access의 부하를 최소화해야 합니다.


Random Access의 부하를 줄이는 방법은 네가지 입니다. 첫번째, 테이블의 종류를 변경하는 방법입니다. IOT나 클러스터를 이용하면 Clustering Factor가 극단적으로 좋아집니다. 또한 파티션을 이용하면 같은 범위의 데이터를 밀집시킬 수 있습니다. 두 번째, 효율적인 인덱스를 사용하거나 조인방법과 순서를 조정하여 Table Access를 최소화하는 방법입니다. 이 방법은 1번과 2번에서 이미 설명되었습니다. 세번째, 인덱스에 칼럼을 추가하여 Table Access를 방지하는 방법입니다. 예를 들어 Select절의 특정 칼럼 때문에 테이블이 액세스 된다면, 인덱스의 마지막에 그 컬럼을 추가하면 됩니다. 네 번째, 인덱스만 액세스하고 테이블로의 액세스는 모든 조인을 끝내고 마지막에 시도하여 Random Access의 횟수를 줄이는 방법입니다.



4. Sort나 Hash 작업을 최소화하라.

1,2,3번을 통하여 최적의 Access Path와 Join을 사용했다면, Block I/O의 관점에서는 튜닝이 끝난 것입니다. 하지만 1,2,3번이 모두 해결되었다 해도 Order by나 Group By 때문에 성능이 저하될 수 있습니다. 특히 결과가 많은 경우, sort는 치명적입니다.


인덱스가 sort 되어 있다는 특성을 이용하면 order by 작업을 대신할 수 있습니다. Group By도 sort가 발생하는데 group by 단위와 인덱스의 컬럼이 동일하다면 sort는 발생하지 않습니다. 최적의 인덱스를 사용하면 Access Path를 개선하는 효과뿐만 아니라 Sort의 부하도 없어집니다.


Union All을 제외한 집합연산(Union, Minus, Intersect)를 사용하면 Sort Unique 혹은 Hash Unique가 발생하게 됩니다. Union은 Union All로 바꿀수 없는지 검토해야 하고, Minus는 Not Exists 서브 쿼리를 이용하여 Anti Join으로 바꿀 수 없는지 고려해야 합니다. Intersect는 교집합이므로 조인으로 바꿀 수 있는지 검토해야 합니다아주 가끔 Distinct를 사용한 SQL이 눈에 띄는데 이 또한 Sort unique 혹은 Hash Unique를 발생시킵니다. 모델러나 설계자의 문의하여 Distinct를 제거할 방법이 없는지 문의해야 합니다.


Oracle 10g 부터는 Hash Group By가 발생할 수 있는데, 이미 적절한 인덱스를 사용하는 경우라면 Hash Group By를 사용할 필요는 없습니다. 이런 경우 NO_USE_HASH_AGGREGATION 힌트를 사용하면 Sort Group By 로 바꿀수 있습니다. 이렇게 해주면 실행계획에 "SORT GROUP BY NOSORT" Operation이 발생하며, Sort나 Hashing 작업이 전혀 발생하지 않습니다. Group By의 부하를 해결하는 또 하나의 방법은 스칼라 서브쿼리를 사용하는 것입니다. 조인을 사용하면 Sum 값을 구하기 위해 Group By가 필수적입니다. 하지만 스칼라 서브쿼리를 사용하면 Group By를 사용하지 않고도 sum이나 Min/Max 값을 구할 수 있습니다. 또한 분석함수의 Ranking Family(rank, dens_rank, row_number)를 최적화된 인덱스와 같이 사용하면 Group By나 Sort를 하지 않고도 Min/Max 값을 구할수 있습니다. 이때는 실행계획에 "WINDOW NOSORT" Operation이 발생합니다.



5. 한 블록은 한번만 Scan하고 끝내라.

같은 데이터를 반복적으로 Scan하는 SQL이 의외로 많습니다. 대표적인 경우가 Union All로 분리되었지만 실제로는 그럴 필요가 없는 경우입니다. 예를 들어 Where 절에 구분코드가 1일 때, 2일 때, 3일 때 별로 SQL이 나누어져 있는 경우 입니다. Where 절을 구분코드 in (1, 2, 3)으로 처리하고, Select절에서 Decode나 Case 문을 사용하여 구분코드별로 처리해 준다면 Union All은 필요 없습니다. Unon All을 사용하는 또 한가지의 경우는 Sub Total(소계)와 Grand Total(총계)를 구해야 하는 경우입니다. 이 경우도 Rollup/Cube나 Grouping Sets를 Group By절에 사용한다면 소계나 총계를 위한 별도의 Select문을 실행시킬 필요는 없습니다. 1~4번의 과정은 SQL문의 변경이 없거나 최소화됩니다. 하지만 5번의 경우는 SQL을 통합시켜야 하기 때문에 시간이 많이 소모되며, 많은 사고가 요구되는 창조적인 과정입니다. 여기까지 했다면 진행되었다면 원본 SQL 자체의 튜닝은 완료된 셈입니다.



6. 온라인의 조회화면이라면 페이징 처리는 필수다.

부분범위 처리를 해야합니다. 물론 전체 건을 처리해야 하는 경우는 있을 것입니다. 하지만 조회화면이라면 몇 십만건 혹은 몇 만건이나 되는 결과를 모두 볼 수 없습니다. 따라서 볼 수 있는 단우로 끊어서 출력해야 합니다. 예를 들어 결과 건수가 10만건이라고 해도 최초의 50건을 화면에 먼저 뿌린다면 1,2,3,4 번에서 설명했던 모든 부하(Block I/O의 부하, 조인의 부하, Random Access의 부하, Sort의 부하)를 한꺼번에 감소시킬 수 있습니다. 따라서 가능하면 개발자를 설득하거나 책임자를 설득하여 페이징처리를 하는 것이 바랍직합니다.


페이징 처리를 해도 효과를 볼 수 없는 몇가지 예외가 있습니다. 분석함수를 사용하건, Connect By + Start With를 사용한다면 페이징 처리의 효과는 없습니다. 분석함수의 경우 인라인뷰의 외부로 뺄 수 있다면 부분범위 처리가 가능합니다. Connect By + Start With를 사용한 경우는 부분범위처리가 불가능합니다. 인덱스의 구성을 적절히 하여 Sort를 대신해야 합니다.



7. 답이 틀리면 안된다. SQL을 검증하라

7번은 SQL 자체를 튜닝하는 것은 아닙니다. 하지만 7번을 튜닝 방법에 추가한 이유는 있습니다. 튜닝을 하였음에도 답이 틀린다면, 튜닝을 하지 않은 것보다 못합니다. 그러므로 튜닝 후에 답이 옳은지 항상 검증해야 합니다. 1번~7번 중에 가장 중요한 것이 7번입니다.



방법론 정리

1. 적절한 인덱스를 사용하여 Block I/O를 최소화하라.

2. 조인방법과 조인순서를 최적화 하라.

3. Table Access(Random Access)를 최소화하라.

4. Sort나 Gash 작업은 최소화하라.

5. 한 블록은 한번만 Scan하고 끝내라.

6. 온라인의 조회화면이라면 페이징처리는 필수이다.

7. 답이 틀리면 안된다. SQL을 검증하라.



만약 1~7번을 모두 적용할 수 있는 경우임에도 불구하고 하나라도 빠진다면 그것은 최적화된 SQL이 아닙니다. 물론 튜닝을 할때 위의 1~6번을 항상 적용할 수 있는 것은 아닙니다. 경우에 따라서는 하나만 적용될 수도 있고, 두 개만 적용할 수 있는 SQL도 있습니다. 하지만 1~6을 모두 적용할 수 있는지 꼼꼼히 살펴야 합니다.


위 항목들은 튜닝의 기본 중에 기본입니다. 이것들만 알아도 온라인 조회화면에 사용하는 SQL을 튜닝하는데 어려움이 없을 것입니다. 다시 말해 90%는 해결할 수 있습니다. 그렇다면 나머지 10%는 상황별로 다르게 처리돕니다. 또한 그것들은 책이나 매뉴얼에 나와있지 않기 때문에 경험치이거나 실험과 연구의 결과로 알아내는 것들입니다.

블로그 이미지

낭만가을

,

JVM이란?

프로그래밍/OS 2018. 4. 21. 12:09


우선 JVM이란?


자바 가상 머신이라 부른다.

자바와 운영체제사이에서 중계자 역할을 한다.

자바가 운영체제 종류에 영향받지 않고 돌아갈 수 있도록 한다.

메모리 관리를 자동으로 해준다 (GC)


여기서 중요한 것은 운영체제와 플랫폼 종류에 의존적이지 않고 독립적으로 JAVA프로그램이 실행된다.






위 그림을 설명하면...


Java Compiler(자바 컴파일러)에 의해 Java source(.java) 파일은  Byte code로 변환된다.


Class Loader(클래스 로더)는 이 변환된 Byte code(.class) 파일을 JVM 내로 class를 로드하고 Link작업을 통해 

배치 등 일련의 작업을 한다. 또 런타임시 class를 load한다.


Execution Engine(실행 엔진) 은 Class Loader를 통해 JVM 내부로 넘어와 Runtime Data Area(JVM 메모리)에 배치된 

Byte code들을 명령어 단위로 실행시킨다.


GC(Garbage Collector)는 어플리케이션이 생성한 객체의 생존 여부를 판단하여, 더이상 참조되지 않거나 null 인 객체의 메모리를 해체시켜

메모리 반납을 한다. 


Runtime Data Areas(JVM 메모리) 런타임 데이터 영역은 JVM메모리로 Java 어플리케이션이 실행하면서 할당받은 메모리영역이다.


세분화하자면 6개(Method, Runtime Constant Pool, Heap, Stack, PC Register, Native Method Stack)의 영역으로 나뉜다.




출처 : http://hoonmaro.tistory.com/19



6개 메모리 영역의 용도와 특징


(화질이 좋지 않으니 클릭해서 보시길.)



끝으로 Heap Area를 좀 더 살펴보면....


Young Generation : 객체가 생성될 때 저장된다. 즉 막 생성된 객체들의 인큐베이터이다. 생성된 기간이 흐르고, 우선순위가 낮아지면

                          Young 세대의 객체들은 Old세대로 이동하게 된다. 이 영역에서 객체가 사라질 때는 Minor GC 수행된다.


Old(Tenured) Generation : Young Generation 영역에 있는 객체가 오래되서 저장되는 공간이다.

                                   이 영역에서 객체가 사라질 때는 Major GC(Full GC) 수행된다.


Permanent Generation :  클래스 로더에 의해 로든되는 클래스, 메소드 등에 대한 메타 정보가 저장되는 영역으로 JVM에 의해 사용된다.                                  리플렉션을 사용하여 동적으로 클래스가 로딩되는 경우에 사용된다. 



또 Method와 Runtime Constant Pool , Heap 영역은 모든 스레드에서 공유할 수 있다.

블로그 이미지

낭만가을

,