권순용의 DB 이야기
목록 쿼리를 최적화하자. 5부. 1 3 99,999+

by axiom 목록쿼리 최적화 n-Row 처리 [2013.10.07]


목록 쿼리의 사용은 매우 많은 것이 사실이다. 우리 주변을 보면 매우 많은 곳에서 목록 쿼리를 사용하고 있다. 이러한 목록 쿼리를 최적화하는 것은 절대 쉬운 일은 아니다.

하지만 이와 같은 목록 쿼리를 최적화하지 않고 성능을 최적화할 수 없는 것도 현실이다. 그렇기 때문에 더욱 많은 경우의 목록 쿼리를 연구하고 최적화해야 할 것이다.

지난 강의에 언급했던 조인을 이용한 목록 쿼리의 최적화는 매우 중요한 내용일 것이다. 이번 시간에는 이미 언급했던 여러 가지 목록 쿼리에 대한 단점을 확인해 보고 이를 해결하기 위한 방법을 몇 회에 걸쳐 연구해 보기로 하자.

n-Row 처리를 수행하는 목록 쿼리도 비효율은 존재한다

어떤 게시판의 전체 목록 데이터가 1만 건이며 페이지마다 보여주는 데이터는 20건이라고 가정한다면 전체 페이지는 5,000 페이지가 될 것이다.

n-Row 처리 방식으로 수행하는 목록 쿼리라면 1페이지를 액세스하기 위해서는 20개의 데이터를 액세스 해서 20개의 데이터 모두를 화면에 추출하게 되므로 비효율이 발생하지 않게 된다.

그렇다면 게시판에서 두 번째 페이지는 어떻게 수행되는가? 두 번째 페이지는 40건을 액세스해서 그 중 밑에 있는 20건을 화면에 추출하게 된다.

위와 같이 두 번째 페이지는 40개의 데이터를 액세스해서 그 중 순번 21부터 40까지를 추출하게 된다. 두 번째 페이지부터는 앞 페이지의 데이터까지 추가로 추출해야 한다. 그 이유는 ROWNUM은 무조건 1부터 시작하기 때문이다.

'ROWNUM BETWEEN 21 AND 40'과 같은 문법은 존재하지 않는다.

ROWNUM은 추출되는 데이터의 번호이므로 첫 번째 데이터가 추출되지 않고 다음 데이터가 추출될 수 없기 때문에 ROWNUM 값이 1이 존재하지 않는다면 ROWNUM의 값이 2인 데이터는 추출될 수 없기 때문이다.

SELECT 종류, 제목, 도착시간, 크기
  FROM 
     ( 
       SELECT /*+ INDEX_DESC(메일,아이디_도착시간) */
              ROWNUM 순번, 번호, 종류, 제목, 도착시간, 크기
         FROM 메일
        WHERE 메일_아이디 = 'KKK'
          AND ROWNUM <= 40
     )
 WHERE 순번 BETWEEN 30 AND 40;

또 하나의 문제는 40건의 데이터 중 순번이 21부터 40까지의 데이터를 추출하기 위해서는 순번을 할당한 인라인 뷰의 40개의 데이터에 대해 주 쿼리에서 순번 21부터 40까지의 데이터를 바로 액세스하는 것이 아니라 순번 1번부터 다시 액세스해야 한다는 것이다.

이는 순번 컬럼이 가공된 컬럼이므로 인덱스가 존재할 수 없기 때문이다.

결국, 두 번째 페이지는 20건의 불필요한 데이터를 액세스하게 되며 세 번째 페이지에 대해서는 40건의 데이터를 불필요하게 액세스하게 된다.

마지막 페이지인 5,000번째 페이지는 원하는 순번을 찾아 20건을 추출하기 위해 9,980건의 데이터를 불필요하게 액세스해야 한다.

결국, 1페이지가 아니라 2페이지부터는 앞 페이지의 데이터를 액세스해야 하며 순번을 할당한 후에도 주 쿼리에서 다시 한 번 앞 페이지의 데이터를 액세스해야 한다는 것이다.

위와 같은 현상에 의해 뒤 페이지로 이동할수록 성능 저하가 발생하게 된다. 그렇다면 이러한 성능 저하를 제거하기 위해서는 어떻게 목록 쿼리를 수행해야 하는가?

n-Row 처리로 목록 쿼리를 작성하는 것만으로도 많은 부분에서 성능을 보장 받을 수 있었지만 그래도 문제는 존재하게 된다.

뒤 페이지로 이동할 경우 성능이 저하되는 비효율을 제거한다면 성능은 더욱 향상 될 것이다.

뒤 페이지로 이동함에 따라 발생하는 비효율을 제거하는 것은 가능하다. 하지만, 이와 같이 프로그램을 개발했을 경우 많은 개발자들이 운영을 힘들어 하는 것을 보아 왔다.

이런 이유에서 뒤 페이지로 갈수록 비효율이 발생하는 것을 제거하기 위해서는 고민해야 할 사항이 발생하게 된다.

과연 성능의 최적화와 관리의 용이성 가운데 무엇이 우선인가라는 점이다. 성능도 중요하지만 관리를 고려하지 않을 수 없으며 관리도 중요하지만 성능을 고려하지 않을 수는 없을 것이다.

대부분의 목록 쿼리는 뒤 페이지로 이동할 경우 비효율이 생기더라도 n-Row 처리 방식으로 수행되는 목록 쿼리라면 우리가 원하는 어느 정도의 성능을 보장 받을 수 있게 된다.

그렇지 못한 목록 쿼리에 대해서만 뒤 페이지로 이동 시 발생하는 성능 저하까지 제거하는 것을 고려하는 것이 유리할 것이다.

이와 같은 규칙에 의해 목록 쿼리를 작성한다면 대부분의 목록 쿼리가 n-Row 처리로 수 행되므로 쉽게 관리할 수 있으며 단지 몇 개의 목록 쿼리만 주 의한다면 관리가 아주 어렵지만은 않을 것이다.

n-Row 처리를 수행하는 목록 쿼리의 비효율을 제거하자

n-Row 처리를 수행하는 목록 쿼리에 대해 뒤 페이지로 이동하면 발생하는 비효율을 제거하기 위해서는 무엇이 필요한가?

무엇이 필요한지만 알 수 있다면 우리는 모든 문제를 쉽게 해결 할 수 있다. 각각의 페이지에서 추출하고자 하는 데이터만을 추출하기 위해서는 각 페이지에서 추출되는 첫 번째 데이터의 속성 을 알아야만 할 것이다.

해당 데이터만 바로 알아낼 수 있다면 우리는 모든 것을 해결할 수 있는 열쇠를 획득하게 되는 것이다.

예를 들어, 20건씩 데이터를 보여주는 목록 쿼리에서 전체 데이터는 10,000건의 데이터이며 이중 열 번째 페이지의 데이터를 추출한다고 가정해 보자.

전체 10,000건의 데이터에 대해 정렬을 수행하고자 하는 컬럼인 도착시간 컬럼으로 정렬해 순번 을 할당한다면 1부터 10,000까지의 순번이 할당될 것이다.

이중 열 번째 페이지를 구성하는 순번은 몇 번인가?

누구나 쉽게 열 번째 페이지를 구성하는 순번을 찾을 수 있을 것이다. 열 번째 페이지를 구성하는 순번은 181번부터 200번까지의 순번일 것이다. 또한, 열 번째 페이지의 첫 번째 데이터는 181번 순번을 가지는 데이터가 될 것이다.

결국, 181번인 순번의 데이터를 미리 알 수 있다면 또는 181번 순번부터 200번 순번까지의 데이터를 미리 알 수 있다면 n-Row 처리 방식으로 수행하는 목록 쿼리에서 발생하는 뒤 페이지로 이동할 경우 발생하는 비효율을 제거할 수 있을 것이다.

그렇다면 각 페이지를 구성하는 데이터의 정보를 어떻게 추출할 것인가가 문제일 것이다.

누구나 쉽게 열 번째 페이지를 구성하는 순번을 찾을 수 있을 것이다. 열 번째 페이지를 구성하는 순번은 181번부터 200번까지의 순번일 것이다. 또한, 열 번째 페이지의 첫 번째 데이터는 181번 순번을 가지는 데이터가 될 것이다.

아래의 쿼리를 확인해 보자.

SELECT CEIL(COUNT(메일_아이디)/20)
  FROM 메일
 WHERE 메일_아이디 = 'KKK'

목록 쿼리에서 실제 목록 쿼리가 수행되기 전에 수행되어야 하는 모든 데이터의 건수를 추출하는 COUNT 쿼리이다.

보통의 목록 쿼리는 한 페이지에 보여주는 데이터가 제한된다. 보통의 게시판은 해당 페이지의 데이터를 보여주며 또한 해당 게시판이 몇 페이지로 구성되어 있는지를 보여주게 된다.

해당 페이지를 보여주는 부분은 목록 쿼리로 해결하게 된다. 그렇다면 해당 게시판이 몇 페이지로 구성되어 있는지를 보여주기 위해서는 어떻게 해야 하는가?

해당 게시판이 몇 페이지로 구성되어 있는지를 보여주기 위해서는 조건을 만족하는 데이터가 몇 건 인지를 알아야 할 것이다.

그렇기 때문에 위와 같은 COUNT 쿼리가 수행되어 CEIL 함수 등을 이용해 페이지 수를 추출하게 된다.

이처럼 페이지 수를 보여주는 게시판의 경우에는 COUNT 쿼리와 목록 쿼리는 쌍으로 수행된다.

이러한 개념을 통해 n-Row 처리의 목록 쿼리를 최적화해야 할 것이다.

중요한 부분은 목록 쿼리 전에 대부분의 목록 쿼리는 COUNT 쿼리가 수행된다는 것이고 이를 이용한다면 각 페 이지의 첫 번째 Row에 대한 정보를 좀 더 쉽게 찾을 수 있을 것이라는 점이다.

이에 대한 부분은 다음 시간에 계속 언급하도록 하겠다.

목록 쿼리의 최적화는 기술도 아닌 논리에 달려 있다. 논리를 어떻게 설계하는가에 따라 우리는 쉽게 목록 쿼리를 최적화 할 수 있을 것이다.

참고링크

- 강좌 URL : http://www.gurubee.net/lecture/2633

- 구루비 강좌는 개인의 학습용으로만 사용 할 수 있으며, 다른 웹 페이지에 게재할 경우에는 출처를 꼭 밝혀 주시면 고맙겠습니다.~^^

- 구루비 강좌는 서비스 제공을 위한 목적이나, 학원 홍보, 수익을 얻기 위한 용도로 사용 할 수 없습니다.

by 아발란체 [2013.10.08 14:45:17]

ㅡ ㅁ ㅡ)ㆀ 4부를 보며 더 무지무지 궁금하던 부분인데...
6부가 무지 기대 됩니다.
보물 같은 강좌 ~ 감사합니다.


by 김정식 [2013.10.08 15:14:50]

n-Row 서브쿼리에서는 ROWID 만 가져오고 rowid로 self join 해서 데이터를 가져오는 방법일 거 같네요


by 광이 [2018.10.25 14:20:54]

6부는 어디있나요?

댓글등록
SQL문을 포맷에 맞게(깔끔하게) 등록하려면 code() 버튼을 클릭하여 작성 하시면 됩니다.
로그인 사용자만 댓글을 작성 할 수 있습니다. 로그인, 회원가입