iWiz ShareBase

IT Specialist 윤태현의 iWiz ShareBase는 IT뿐 아니라 각종 잡다한 지식들을 함께 나누는 지식공유 커뮤니티입니다.

iWiz,ShareBase,윤태현,Java,JSP,EJB,IT,정보기술,웹프로그래밍,PHP,ASP,DBMS,MySQL,서버,네트워크,server,network,WAS,웹애플리케이션,블로그,blog,웹서버,DB,오라클,oracle,mysql,JRun,웹로직,톰캣,tomcat,아파치,자동차,EF쏘나타,로또 6/45

갤러리 Pixelgrapher.com | 로또 6/45 번호생성 및 통계 데이터 | 전체기사보기 | 전체글 #1 | 전체글 #2 | 전체글 #3 | 전체글 #4 | 전체글 #5 | 전체글 #6 | 전체글 #7 | 전체글 #8 | 전체글 #9 | 전체글 #10 |
HOME iWiz
ShareBase
Remember 0523 & 0818
지식은 나눌수록 커집니다 - iWiz's ShareBase
웹프로그래밍(기타) PHP, ASP, Perl, CGI 등 각종 웹프로그래밍에 관한 자료들입니다.


  iWiz(2004-01-04 22:37:12, Hit : 7542, Vote : 56
 http://www.wz.pe.kr

JDBC를 이용한 페이징 구현 방식별 성능 측정


이 문서는 자바에서 JDBC를 이용해 게시판 등의 페이징 구현시 가장 빠르고 효율적인 방법을 찾기위해 각 방법을 테스트한 결과를 정리한 것입니다.

■ 들어가기전에

1. ResultSet 타입과 Cursor 타입

JDBC에서 지원하는 ResultSet의 종류는 2가지가 있습니다.

  • ResultSet.CONCUR_READ_ONLY : 읽기전용 ResultSet
  • ResultSet.CONCUR_UPDATABLE : 수정가능 ResultSet

위 두가지 ResultSet중에서 당연히 READ_ONLY가 속도가 더 빠를뿐더러, 일반 웹프로그래밍 특성상 ResultSet내에서 어떤 데이터를 수정하는 경우는 거의 찾아보기 힘듭니다.  Statement 생성시 특별히 지정하지 않으면 기본값은 READ_ONLY가 됩니다.

그리고 ResultSet에서 사용할 수 있는 커서에는 세가지가 있습니다.

  • ResultSet.TYPE_FORWARD_ONLY : 앞으로만 움직일 수 있는 커서
  • ResultSet.TYPE_SCROLL_INSENSITIVE : 앞뒤로 움직일 수 있으나, ResultSet 생성 이후에 발생한 업데이트(레코드 추가, 삭제) 등은 반영되지 않음.
  • ResultSet.TYPE_SCROLL_SENSITIVE : 앞뒤로 움직일 수 있으며, ResultSet 생성 이후에 발생한 업데이트(레코드 추가, 삭제)도 ResultSet에 반영.

물론 속도는 FORWARD ONLY > SCROLL_INSENSITIVE > SCROLL_SENSITIVE 순으로 빠릅니다.  역시 Statement 생성시 커서 타입을 지정하지 않으면 기본값으로 FORWARD_ONLY가 사용됩니다.

2. 웹프로그래밍의 특수성

일반 Client/Server 애플리케이션 등에서는 DB 작업시 프로그램을 종료하기 전까지는 계속 DB 커넥션을 잡고있는 경우가 많으며, 특히 레코드를 앞뒤로 오가며 작업하는 경우가 많기 때문에 자유롭게 ResultSet을 탐색하며, 중도에 레코드가 업데이트된 경우 이것도 실시간으로 반영되여야 하며, 때로는 탐색중 업데이트가 필요하기도 합니다.  따라서 다양한 기능을 갖는 ResultSet과 커서의 지원 여부가 중요합니다.

이에 반해 웹애플리케이션 같은 경우는 Connection 및 ResultSet이 한 페이지내에서 모든 처리가 끝나며, 걸리는 시간도 수 초에 불과하고, DB 관련 작업도 개발자가 설계한 의도내에서 단순하게 처리된다고 볼 수 있습니다.  따라서 ResultSet과 커서의 다양한 기능들을 활용하는 빈도가 매우 낮습니다.

특히 웹애플리케이션 같은 경우는 사용자의 다음 행동을 예측하기 어렵기 때문에 커넥션을 여는 것에서부터, DB 작업, 커넥션 닫기를 한 페이지내에서 모두 완결해주어야 합니다.  따라서 페이징 같은 경우는 매우 단순한 작업임에도 페이지가 넘어갈때마다 동일한(또는 비슷한) 작업- 커넥션얻기☞ 레코드 수 파악 ☞ 페이지계산 ☞ 현 페이지 위치에 해당하는 레코드로 이동 ☞ Fetch ☞ 커넥션 반환 - 을 반복해주어야 하는 비효율성이 있습니다. (C/S애플리케이션에서는 페이지 이동시 그냥 커서만 옮겨주면 되죠)  한때 어떤 ASP 개발자는 이런 비효율성을 극복하기 위해 ResultSet을 세션에 보관해 사용자의 페이지 이동시 커서만 움직이면 된다고 주장하는 것을 봤는데, 이는 매우 위험천만한 발상일 것입니다.

아래 테스트는 JDBC의 각 커서 타입의 특성과 더불어 웹애플리케이션의 특성도 함께 감안하면서 읽으시기 바랍니다.

■ 테스트에 사용한 방법

일단 테스트에는 244,619개의 레코드가 존재하는 오라클 DB의 카탈로그 정보 테이블을 WHERE 조건없이 전체 레코드를 사용했습니다. (단, co_code, co_lang, ca_no 순으로 ORDER BY는 주었습니다)  한 페이지당 20개의 레코드가 출력된다고 전제했을때 전체 페이지 수만 해도 12,231 페이지에 달하므로 각 방식별 성능 차이를 어느정도 느낄 수 있을 것입니다.

테스트는 아래 세가지 방식을 사용해 첫페이지와  중간페이지, 그리고 마지막 페이지를 여는데 걸리는 시간을 각각 측정했습니다. (아래 명칭은 제가 마음대로 지은 명칭입니다)

방    법

설    명

전후진커서

이것은 SCROLL_INSENSITIVE 커서를 사용했습니다.
먼저 총 레코드 수를 알기 위해 가장 마지막 레코드로 이동을 해 레코드 번호를 얻습니다.
그 후 프로그램에서 페이지 계산을 한 후 absolute메소드로 현재 페이지에 해당하는 레코드로 바로 이동합니다.
그리고 실제 레코드를 Fetch 합니다.

전진커서

여기에서는 FORWARD_ONLY 커서를 사용했습니다.
총 레코드 수를 알기 위해 마지막으로 이동하면 다시 뒤로 갈 방법이 없기 때문에 ResultSet 생성 이전에 미리 COUNT 쿼리로 레코드 수를 파악합니다.
프로그램에 의한 페이지 계산을 한 후 setMaxRows 메소드를 통해 첫 레코드부터 현재 페이지의 마지막에 해당하는 레코드까지만 ResultSet에 담습니다.
그 후 현재 페이지에 해당하는 레코드로 이동하기 위해 프로그램에서 next() 메소드를 loop로 돌립니다. (FORWARD_ONLY에서는 absolute나 relative 메소드를 사용한 커서 이동이 불가).  그리고 실제 레코드를 Fetch 합니다. 

혼합방식

이것은 SCROLL_INSENSITIVE 커서를 사용했습니다.
단, 레코드 수를 알기위해 끝까지 이동하는 대신 전진커서처럼 미리 COUNT 쿼리를 통해 레코드 수를 파악하고 필요한 레코드들만 MaxRows를 이용해 실제 ResultSet에 담습니다.
현재 페이지로 이동하는 방법은 전후진커서처럼 계산된 레코드 번호로 absolute 메소드를 이용해 바로 이동하는 방식을 사용했습니다.

이 세가지 방식의 차이점은 사용하는 커서 종류와 ResultSet의 전체 레코드 수를 얻는 방법, 그리고 현재 페이지에 해당하는 레코드로의 이동 방법에 차이가 있습니다.  단, 전진커서와 혼합방식에서는 미리 COUNT 쿼리를 통해 레코드 수를 알고, 이를 통해 현재 페이지의 마지막 레코드 이후의 레코드들은 필요가 없음을 알고 있기 때문에 Statement에서 setMaxRows 메소드를 이용해 ResultSet에 담을 레코드 수를 제한할 수가 있다는 점에 차이가 있습니다.

■ 테스트 결과 (소요시간)

구  분

전후진커서

전진커서

혼합방식

첫페이지
(1)

레코드 수 카운트

17.406초

0.467초

0.463초

현페이지로 커서 이동

00.000초

0.004초

0.004초

총 DB처리 소요시간

17.408초

0.472초

0.467초

중간페이지
(6,115)

레코드 수 카운트

17.928초

0.433초

0.444초

현페이지로 커서 이동

00.000초

6.458초

8.578초

총 DB처리 소요시간

17.929초

6.894초

9.024초

끝페이지
(12,231)

레코드 수 카운트

17.473초

0.462초

0.413초

현페이지로 커서 이동

00.000초

13.128초

16.662초

총 DB처리 소요시간

17.474초

13.593초

17.078초

위 수치의 측정은 서비스 시간중 이루어졌기 때문에 서버 부하 등의 요인에 따라 다소 오차가 발생할 수도 있습니다.  따라서 소수점 이하 자리는 버리고 보시면 됩니다.  그리고 같은 색으로 마킹된 셀들의 수치는 기본적으로 동일한 로직이므로, 같은 값을 갖는다고 보면 됩니다.

■ 결과 설명

 측정결과 전진커서가 가장 빠른 속도를 보여줬으며, 그 다음으로는 혼합방식이었으며 전후진커서는 가장 느린 속도를 보여줬습니다.  특히 웹상에서 가장 빈번하게 사용되는 첫페이지를 비롯한 앞쪽 페이지들의 접근 속도에서 전진커서와 혼합방식은 1초 미만의 처리 속도를 보여준데 반해 전후진커서는 17초 이상의 속도를 보여줘 웹상에서 사용하는 것은 부적절함을 알 수 있습니다.  레코드의 끝까지 탐색하게 되는 마지막 페이지에서는 격차가 좁혀지게 되는데, 전후진커서와 혼합방식은 거의 비슷한 시간이 소요되며 전진커서가 4초가량 더 빠른 속도를 보여주고 있음을 알 수 있습니다.

특이한 점은 현재 페이지의 레코드로 이동할 때 전후진커서와 혼합방식이 동일한 커서방식과 함께 동일하게 absolute 메소드를 사용하는데도 서로 속도가 차이가 나는 것을 볼 수 있는데, 전후진커서 방식의 경우 처음에 총 레코드 수를 얻기 위해 마지막 레코드까지 이동하는 과정에서 일종의 인덱싱이 일어나 그 이후의 커서 이동은 매우 빠른 속도로 이뤄진다는 것을 짐작할 수 있습니다. (그래서 처음 레코드 수 카운팅을 위해 마지막 레코드까지의 이동할때의 속도가 느린 듯 합니다)  이런 특성은 C/S 애플리케이션에서는 뛰어난 성능을 보여주겠지만, 한 페이지내에서 모든 처리가 끝나버리는 웹애플리케이션에서는 오히려 역효과를 일으키는 것 같습니다.

최종적으로 결론을 내린다면 전진커서가 프로그램상에서 처리하는 부분이 조금 더 있긴 하지만, 속도 부분에서는 가장 효율적이라고 볼 수 있습니다.  특히 웹상에서 가장 빈번하게 접근되는 페이지가 초기 몇 페이지라는 관점에서 볼 때는 그 효용 가치가 더욱 높아질 것이라고 봅니다.

<예외 상황>

단, 한가지 예외 상황이 있다면 고비용을 요구하는 비효율적인 쿼리문에서는 결과가 다르게 나올 수도 있습니다.  예컨데 복잡한 WHERE절이 있고, INDEX 등도 제대로 잡혀있지 않거나 정확히 사용할 수 없는 구조라면 전후진커서와 전진커서간의 격차가 줄어들거나, 또는 전후진커서가 더 효과적일 가능성이 있습니다.

왜냐면 전진쿼리나 혼합방식은 총 레코드 수 파악을 위해 쿼리를 한번 더 수행해야 하는데, WHERE 절이 복잡하다면 여기에 소요되는 비용이 전후진쿼리에서 마지막 레코드까지 이동하는 비용과 맞먹거나 초과할 수도 있기 때문입니다.

아마 이 경우에는 WHERE절이 복잡하고 비효율적일수록 전후진쿼리와 전진쿼리간의 격차가 좁혀지고, 어느 수준에서는 전후진쿼리가 더 빠른 경우도 나타날 수 있습니다.  (전적으로 WHERE절이 얼마나 많은 비용을 필요로 하느냐에 따라 달려 있습니다)

이 경우에는 상황에 따라 다르긴 하겠지만 혼합방식이 가장 비효율적이 될 확률이 높습니다. (결과적으로 전후진커서와 전진커서 양쪽의 단점이 모두 반영되므로...)

한가지 확실한 것은 이러한 쿼리를 이용한 페이징 구현에서는 전후진쿼리가 전진쿼리에 비해 효과적일 가능성도 있지만, 결과적으로는 어느 방식을 사용하던지 쿼리 수행을 위해 높은 비용을 치뤄야 한다는 점을 간과해서는 안될 것입니다.(특히 웹프로그래밍에서는).

■ MySQL에서의 효율적인 페이징 구현

위의 테스트는 오라클에서 진행된 것이며, MySQL에서는 더 효율적인 페이징 방법이 있습니다

MySQL에 Query를 날릴때부터 아예 SQL에서 LIMIT n, m 구문을 사용해 가져올 레코드 범위를 지정할 수 있습니다.  (n=시작레코드번호, m=마지막레코드번호)  이를 통해 현재 보여지는 페이지의 범위에 해당하는 레코드만 빼낼 수 있기 때문에 가장 빠르고 효율적인 페이징을 구현할 수 있을 것 입니다.

오라클에서도 역시 rownum이라는 pseudo 컬럼을 이용해 비슷한 방법을 구현할 수 있으나, ORDER BY를 이용한 정렬 문제를 비롯해 범위 지정 등의 문제가 있어 자유로운 페이징 구현에 있어서는 적합하지 않습니다. (뷰를 생성해서 해결하는 방법이 있으나 다소 복잡해집니다)


예전에 ASP를 사용할 때도 ADO(Active Database Object)를 사용해 위와 비슷한 테스트를 해봤던 기억이 나는데, 그때도 비슷한 결과가 나왔던 것 같습니다.  따라서 페이징 구현시 커서 사용 방법은 특정 언어나 DBMS, 플랫폼 보다는 어떤 종류의 애플리케이션을 만드냐에 따라 결정된다고 할 수 있을 것입니다.

* iWiz님에 의해서 게시물 이동되었습니다 (2010-02-03 16:57)



62   mod_throttle 모듈을 이용한 사용자 트래픽 제어  iWiz 2006/06/22 9063 0
61   Tomcat-Apache using JK2 connector  iWiz 2004/03/21 7799 41
60   RedHat 9.0에서의 JRun JSP 컴파일러의 문제점  iWiz 2004/01/04 5553 50
59   RedHat 9.0에서의 JRun-Apache 커넥터의 문제점  iWiz 2004/01/04 5187 48
58   JRun 4.0의 튜닝 관련 옵션  iWiz 2004/01/04 5945 68
57   JRun 4.0의 Activity 모니터링 방법  iWiz 2004/01/04 4921 57
56   JRun4.0: DataSource 커넥션풀 관련 옵션 [4]  iWiz 2004/01/04 6664 46
55   JRun에서 JSP 컴파일시 java 파일 생성하기  iWiz 2004/01/04 8068 63
54   JRun의 실제 서비스 운영시 고려사항  iWiz 2004/01/04 6373 44
53   수정된 인터넷 익스플로러에서 상호작용 ActiveX 컨트롤 활성화 가이드  iWiz 2006/03/03 8457 4
52   HTML 특수기호 엔터티(Entity) 테이블 [2]  iWiz 2006/03/03 14183 2
51   웹사이트의 새로운 혁명 Ajax [13]  iWiz 2005/11/22 5870 6
50   MSN 메신저 친구 자동등록 스크립트  iWiz 2004/10/12 6192 35
49   JavaScript MD5 해쉬 생성 함수  iWiz 2004/01/07 9080 35
48   JavaScript로 만든 진법변환 및 보수계산기 [4]  iWiz 2004/01/04 160610 51

1 [2][3][4][5]
 

Copyright 1999-2023 Zeroboard / skin by zero
iWiz ShareBase, ⓒCopyleft by iWiz.  For more information contact .
본 웹사이트에 게시된 이메일 주소가 전자우편 수집 프로그램이나 그 밖의 기술적 장치를 이용하여 무단으로 수집되는 것을 거부하며, 이를 위반시에는 정보통신망법에 의해 형사처벌됨을 유념하시기 바랍니다. [게시일 2004. 1. 31]