Paging Query 처리 방법

  1. Paging 란?
    1. Offset Paging 방식
    2. Cursor 방식

Paging 란?

특정 데이터를 부분적으로 분할해서 데이터를 조회 하는 방식을 말합니다.
예를들어 A 테이블에 100만건이 있다고 존재할때 클라이언트에 데이터를 보낼때 모든 데이터를 보여주는 것 보다 부분적으로 나눠서 데이터를 보내는 것이
DB Server 측면에서 처리시간 및 효율적으로 자원 관리를 할 수 있게 됩니다.

Offset Paging 방식

우선 일반적으로 많이 사용되고 있는 Offset Paging 방식을 알아보도록 하겠습니다.

1
2
3
4
5
6
7
SELECT 
*
FROM
${테이블명}
ORDER BY
${생성 일자 컬럼} DESC
LIMIT 5

처음 부터 10개만 출력 하는 쿼리 문 입니다. (1회차)

1
2
3
4
5
6
7
8
SELECT 
*
FROM
${테이블명}
ORDER BY
${생성 일자 컬럼} DESC
OFFSET ${N 회차}
LIMIT 5

N 번째 부터 그 후 5개 출력 하는 쿼리 문 입니다. 만약 offset 이 3 번째 이라고 하면 3번째 부터 5개 출력 하는 의미 입니다.

간단하게 Offset Paging Query 에 대해서 알아보았는데요. 구현방식은 단조롭지만 단점이 있습니다.
1회차 경우는 인덱스만 잘 활용 한다면 문제가 없지만 이후 2회차 이상 부터는 오히려 DBMS 에 부하가 발생 할 수 있습니다.
이유는 Offset 에 설정한 위치까지 모든 레코드를 순차적으로 조회 후 이제서야 데이터를 조회 하기 때문입니다.
즉 Offset 의 지정된 값이 크면 클 수록 DB Server 에 부하가 발생 될 수 있습니다.

이것은 Offset Paging 사용하지 않고 Table Full Scan 사용하는 것 보다 더 나쁜 성능을 보여줍니다.

Cursor 방식

Offset Paging 방식은 원하는 데이터가 “몇 번째” 에 있다는 데에 집중하고 있다면 Cursor Paging 방식은 우리가 원하는 데이터가 “어떤 데이터의 다음” 에 있다는 데에 집중합니다.
즉 Offset 방식은 N 개의 row 를 skip 한 다음 10개를 가져오고 Cursor 방식은 정적으로 row 다음 부터 10개를 가져오는 방식 입니다.

Cursor Paging 방식 처리 하는 과정에서 여러가지 방식이 있지만 그 중 “데이터 갯수 기반 방식” 중 “범위 조건” 방식에 대해 알아보도록 하겠습니다.

“데이터 갯수 기반 방식” 경우는 배치 작업보다 서비스단에서 주로 사용되는 형식 입니다. ORDER BY 하고 LIMIT 사용하면서 쿼리 작성 하게 됩니다. 또 다른 특징은 1회차 쿼리문하고 N 회차 쿼리문 형태가 다르다는 점 입니다.

1
2
3
4
5
6
CREATE TABLE boards (
no int NOT NULL AUTO_INCREMENT,
updated_at datetime NOT NULL,
PRIMARY KEY (no),
KEY idx_updated_at (updated_at)
)

우선 boards 이라는 테이블을 만들도록 하겠습니다. ‘no’ 컬럼은 PK 값으로 설정하고 updated_at 수정일 컬럼이 있습니다.
이 방식으로 Paging 처리를 한다고 가정 하겠습니다.

1차 범위조건 쿼리.PNG

1회차 조회 방법 입니다. Offset 방식과 다르게 Offset 사용하지 않고 대신 ORDER BY 방식으로 해결합니다. 컬럼 updated_at DESC 방식으로 내림차순으로 하고
PK 컬럼인 no 컬럼을 DESC 방식으로 정렬 합니다.

그리고 부가적으로 조회 조건 WHERE 절을 updated_at 시작 날짜, 종료 날짜 범위를 지정 합니다.

ORDER BY 절에 updated_at 컬럼이 추가된 이유는 성능 최적화에 있습니다. 인덱스를 updated_at 로 지정 했다는것을 알수 있습니다.
ORDER BY 절에 no 컬럼만 지정되어 있다면 조건절 updated_at 범위 탐색 하더라도 ORDER BY no 컬럼이 있기 때문에 정렬은 인덱스를 활용 할 수 없습니다.
그래서 ORDER BY 절에 updated_at 컬럼을 먼저 지정하고 범위 탐색에 바로 적용 해서 인덱스를 활용 할 수 있도록 해야 합니다.

1
2
3
4
5
6
7
SELECT
*
FROM
boards
ORDER BY
updated_at DESC, no DESC
LIMIT 5

1회차 조회시 쿼리문 입니다. updated_at, no 순서대로 내림차순으로 정렬 후 이 중 5개를 먼저 탐색되면 바로 반환 되도록 하는 쿼리 입니다.

1
2
3
4
5
6
7
8
9
SELECT
*
FROM
boards
WHERE
updated_at < '2024-10-16 00:00:06' AND no > 9
ORDER BY
updated_at DESC, no DESC
LIMIT 5

잘못된 2차 범위조건 쿼리.PNG

이후 2회차 이후 조회 방법 입니다. 하지만 이것은 잘못된 방법 입니다.
1회차 조회시 맨 마지막 row 데이터인 updated_at 하고 no 값을 이용해서 그 다음 데이터 출력할려고 했지만 PK 값 no 값 경우 항상 updated_at 처럼 정렬된 데이터로 구성 되지 않습니다.
updated_at 날짜 경우 해당 row 가 어느 컬럼이든 업데이트가 일어나면 현재 날짜로 업데이트가 일어나기 때문에 no 값은 항상 일정한 정렬로 일어나지 않습니다.

이럴 경우 초록색 박스만 출력되고 나머지 데이터는 출력되지 않습니다.

1
2
3
4
5
6
7
8
9
SELECT
*
FROM
boards
WHERE
(updated_at = '2024-10-16 00:00:06' AND no < 9) OR (updated_at < '2024-10-16 00:00:06')
ORDER BY
updated_at DESC, no DESC
LIMIT 5

2차 범위조건 쿼리.PNG

우선 1차 쿼리문 통해 조회된 updated_at 날짜 값 ‘2024-10-16 00:00:06’ 이용해서 같으면서도 no 값 9 보다 작은 값들이 있는지 체크를 먼저 확인을 합니다.
그리고 ‘OR’ 조건문을 이용해 나머지 값인 ‘2024-10-16 00:00:06’ updated_at 날짜 보다 작은 값들을 조회 하도록 합니다.


Copyright 201- syh8088. 무단 전재 및 재배포 금지. 출처 표기 시 인용 가능.

💰

×

Help us with donation