[SringBooot][Mysql] Mysql에서 유사 Cursor 방식으로 페이징 구현
커서방식의 페이징 컨셉만 훑어보고 예제도 보지 않고 혼자 구현한 내용이라 많이 허술합니다..
Mysql에서는 Cursor 방식의 페이징을 지원하지 않는다.
Offset방식은 뒤의 정보를 페이징 요청 할 수록 더 느려지고.
데이터가 많으면 많을 수록 더욱 차이는 급격해 진다.
그러면 어떻게 해결 해야 할까?
마지막에 요청한 정보의 아이디를 기록해서 한번 더 보내주는 것이다.
보통 페이징을 한다면 대표적으로 요청 페이지, 페이지당 요소개수, 정렬방식, 오름차순등 이 4개정도의 파라미터가 필요할 것이다.
하지만 커서방식은 뒤의 정보 검색의 성능 향상을 위해서 전에 요청한 페이지의 마지막 요소값만 더 추가해서 파라미터로 보내준다.
예시로 id와 가격, 이름 만 존재하는 단순한 Item 도메인이 존재한다고 가정하자.
만약 이 데이터가 10만건이 있고 첫번째인 0번째 페이지, 이름순 오름차순을 서버에서 요청 받는다면
100번을 반복 시행했을때 내 컴퓨터에서는 151ms 걸린다.
같은 방식으로 마지막 페이지의 평균 값을 구했을 때 191ms가 걸리게 된다.
사정상 10만건 밖에 넣지 못했지만 데이터가 100만 건이 넘어가고 조인할 테이블이 더 늘어날 수록 더 느려지고
개인 컴퓨터에서는 답답함 마저 느낄 정도이다.
커서방식의 마지막 페이지 요청 결과 부터 보여준다면 평균 155ms로 오프셋 방식의 첫번째 요청보다 조금 느릴 뿐 별 차이는 없게 된다.
그렇다면 커서방식의 구현은 어떻게 할 까?
보기전에 offset 방식의 페이징 쿼리를 보자.
select
item.item_id,
item.name,
item.price
from
item
order by -- 정렬만 동적쿼리
item.name asc,
item.item_id asc
limit 20
offset 99980 --파라미터 받아서 계산
정렬만 동적 쿼리 일 수 있고 단순히 값을 받아 처리한다.
하지만 커서방식에서는 생각해야 할 것들이 좀 더 있다.
이 예시에서는 이름 방식 오름차순, 다음 페이지 요청으로만 한정하겠다.
만약 10페이지 건너 뛰기. 마지막 페이지 바로가기, 이전 페이지등을 추가 한다면 좀 더 복잡해 질 수 있기 때문이다.
만약 전에 사용자가 중복 될 수 있는 컬럼인 이름으로 오름차순으로
4998페이지를 요청했고 지금 4999 페이지를 요청 받았을때
4998페이지의 마지막 값의 이름이 "TEST9998"이라면
4999페이지의 이름에는 "TEST9998"이 들어 갈 수도 있고 들어가지 않을 수도 있다.
그리고 그 전전 페이지인 4997에도 "TEST9998" 이라는 이름이 들어갈 수 있는데 이전페이지의 값은 제외 되어야한다.
이점을 고려한다면
id는 id를 제외한 어떤 컬럼으로 정렬하든 오름차순 또는 내림차순으로 항상 정렬 되어야 한다. 본인은 오름차순으로 정렬했다.
그렇다면 이미 찾은 값을 또 찾거나 아직 검색하지 않은 값을 찾지 못하는 것을 방지할 수 있다.
이를 반영 하여 where절에는 "값은 같지만 ID가 더 큰경우 or 값이 더 큰경우" 를 추가해준다.
전페이지의 마지막값은 서브쿼리로 찾을 수도 있고, 전의 값을 프론트에서 받을 수도 있고, 다른 쿼리에서 한번 찾아 그 값을 사용 할 수도 있다.
나는 마지막 값의 id만 받아 서브쿼리로 찾는 방식을 적었다.
select
item.item_id,
item.name,
item.price
from
item
where
-- 마지막 값 95492와 컬럼값이 같으면서 id가 더 큰 경우 (id를 오름차순으로 정렬하므로 더 크게 된다)
item.name = (
select
item_sub.name
from
item item_sub
where
item_sub.item_id = 95492
)
and item.item_id > 95492
or
-- 마지막 값의 컬럼값보다 더 큰 경우
item.name > (
select
item_sub2.name
from
item item_sub2
where
item_sub2.item_id=95492
)
order by
item.name asc,
item.item_id asc --어떤 정렬을 요청 받든 항상 일정한 기준으로 정렬
limit 20
요약
1. 프론트와 백엔드에서 마지막 값에 대한 변수를 하나 더 추가한다.
2. 어떠한 정렬기준으로 요청 받든 id로 항상 일정하게 정렬한다.
3. 그 값을 찾아 where절에 ID 가 크면서 값이 같은 경우 이거나 값이 큰 경우를 넣는다.
4. 이 로직을 각자 ORM에 맞춰 동적으로 설계한다.
주의
이전페이지를 찾을 때는 비슷하지만 다르다.