Limiting Query Result : rownum vs ResultSet

Tags:

자바 질문이라기 보다는 디비 쿼리에 관한 질문인데요. 아직 디비에 대해서는 지식이 부족해서 참으로 헷깔립니다. rownum 이나 top 등을 이용해 처음 몇번째 행이나 중간 얼마만큼의 행만 가져올 수 있는 기능이 대부분의 데이터베이스에서 지원되는데요. 저 기능을 쓰지 않고 커서의 위치만 이동하거나, 처음 몇 행만 페치해 오는 것과 어떤 경우에서 어떤 이유로 퍼포먼스상의 차이가 발생하는지 궁금합니다. 지금까지는 특정 경우에는 별다른 손해 없이 저런 기능을 사용하지 않고도 원하는 결과를 얻을 수 있다고 생각해 왔는데요.

그 특정 경우란 것이 구체적으로 어떤 경우인지 생각하기도 어렵고 실제로 맞는지도 알 수가 없네요.

고수님들의 답변 부탁드리겠습니다-


what we call human nature in actually is human habit.
— Essays – http://gleamynode.net/
Technical – http://tech.gleamynode.net/
Projects – http://projects.gleamynode.net/

[And I said..]

희승님께서 질문하신 내용에 대해서는 제가 알고있는대로 설명해드리겠습니다.

ResultSet은, 질의를 한 결과 집합입니다. 따라서 그 결과집합은

(1) 질의를 수행한뒤에 전부가 jsp쪽으로 날라오거나,
(2) rs.next()를 할때마다 한행씩 돌려주거나
(3) 뭉텅이씩 읽어오거나(예를들면 5개씩 서버측에서 날려준다거나)

중의 하나일 것입니다. 통상은 3번대로 수행됩니다.

그래서 테이블 전체를 읽고, rs.absolute()를 하고 rs.next()를 하면서 읽는다고 하여, 전체 테이블이 몽땅 다 jsp쪽으로 날아오고 그걸 필터링하면서 화면에 출력한다고 말씀하신 이완희님의 설명은 사실과 다릅니다.

JDBC에서는 행 페치 크기를 지정할 수 있고 벤더마다 다음과 같이 지정합니다.

Properties p = new java.util.Properties( );
p.put(“block size”, “512”); // DB2
p.put(“defaultRowPrefetch”, “512”); // 오라클
Connection c = DriverManager.getConnection(“jdbc:dbname:path”, p);

또는 ResultSet이나 Statement에서 이를 조정할 수도 있습니다. 다음 메소드를 사용하면 됩니다.

Statement.setFetchSize(int size)
ResultSet.setFetchSize(int size)

rownum으로 제한하여 db측에서 미리 처리한 것과, rownum 조건을 안주고 rs.absolute()를 한 것의 차이는 행의 갯수를 조정하는 작업을 db에서 하는가 jsp에서 하는가의 차이입니다. db에서 한다면 db쪽에 부담이 갈 것이고 jsp에서 한다면 jsp쪽에 부담이 가겠죠. 만약 별도의 서버라면 양자 택일의 문제가 될 것입니다.

그러나 사실 rownum은 질의를 제한하는데 큰 부담이 걸리지 않을 것입니다. 약간 복잡한 예를 든다면, url/hit_count 가 있는 테이블에서 최상위 5개의 hit를 뽑아온다고 해보죠. (이 예는 자바 퍼포먼스 튜닝 – 한빛에 있는 예입니다.)

그러면 2가지 방법으로 처리할 수 있습니다.

(1)
String sqlQuery = “SELECT url,hits FROM url_hits SORT BY hits”;
Statement statement = connection.createStatement( );
ResultSet resultSet = statement.executeQuery(sqlQuery);
String[ ] urls = new String[5];
int[ ] hits = new int[5];
// 코드를 단순히 하기 위해, 최소한 5개의 URL이 존재한다고 가정한다.
for(int i= 0; i< 5; i++, resultSet.next( )) { url[i] = resultSet.getString(1); hits[i] = resultSet.getInt(2); } (2) select r.url, r.hits from url_hits r where 5 >= (select count(*) from url_hits rr
where rr.hits >= r.hits);

이중 1번 방법은 db단에 부담이 적고, 반면 2번 방법은 db에 부하가 많이 걸리게 됩니다. 이럴 경우에는 앞서 예기한 데이터 페치 크기를 5개의 행 크기로 지정하고 (정확한 페치 크기 계산식은 모릅니다.), 1번 방법을 쓰는게 더 낫다고 합니다.

하지만 이처럼 absolute()를 써가면서 처리한 것이 질의에서 처리한 것보다는 더 복잡하겠죠. (라고 dba 들은 생각하는 것 같습니다. 선언적인 언어가 절차적인 언어보다 편하다는 얘기죠.)

Tom Kyte는 다음과 같은 사항을 제시했습니다. (Tom Kyte’s mantra 라네요)

o if you can do it in a single sql statement – do so.
o if you cannot, do it in as little PLSQL as you can.
o if you cannot, because plsql cannot do it, try some java (eg: sending an email with an attachment)
o if you cannot do it in java (too slow, or you have some existing 3gl code already) do it in C as an extproc.

또다른 예는 게시판의 경우가 있겠죠. 게시판을 작성하면서 질의가 복잡해지는 이유는 전체 행의 갯수를 구하는 문제 때문입니다. 페이징을 해야하고, 또 페이지를 5개씩 잘라서 ‘<이전> 5 6 7 8 9 <이후>‘ 와 같이 페이지를 화면에 보여줘야하죠. 따라서 몇개의 게시물이 있는 지를 세야하는데, 그때 질의를 잘못사용하면 불필요하게 너무 많은 행을 읽습니다. 따라서 행의 갯수를 셀때는 질의를 잘 꾸며서 필요한 행만 읽어서 수를 세야 할 것입니다. 또, 행의 갯수를 세고 나서 또다시 행의 데이터를 읽기 보다는 한번에 행의 데이터를 읽으면서 행의 갯수를 세어, 한번의 게시물 목록 출력에 한번의 sql 만 돌아가야 될 것입니다.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *