unique record의 Paging과 n번의 find
코드를 개선하려고 살펴보던 중 궁금한 점이 생겼다.
MySQL DB에 페이징을 시도할 때, findAll은 limit에 해당하는 레코드의 수를 탐색하고나면 그 즉시 중단할까? 아니면 전부 탐색하고 정해진 데이터 개수만 잘라서 출력해주는 것일까?
결과 먼저 말하자면, 전부 탐색하고 데이터 개수만 잘라서 출력해준다. 지극히 비효율적이다. 그리고 느리다. 아래는 실험 후 찾아본 자료이다.
실험 방식은 다음과 같다.
대상 레코드 : username, nickname - 둘 다 unique 설정이 되어있다.
이와 같은 상황이라면,
findAllByUsernameOrNickname(username, nickname)
다음 쿼리를 시행했을 때 나오는 데이터는 반드시 2개 이하이다.
그러므로,
List<User> user = findAllByUsernameOrNickname(username, nickname)
Page<User> user = findAllByUsernameOrNickname(username, nickname, PageRequest.of(0, 2))
둘의 속도를 비교하면 알 수 있게 될 것이다.
실험을 위해 MySQL을 준비.. 하려고 했으나 사실 못 했다. 이 실험을 위해 로컬 환경에 MySQL을 설치하는게 귀찮았고, RDS를 설정해서 하는 건 돈이 아까웠다.
그래서 H2-Console에서 시행했다. 어차피 비슷한 결과가 나올 것이라고 생각한다.
@loop 1000000 insert into user(username, nickname, password) values(concat('user',?), concat('nickname',?), '1234') ;
H2-Console에 100만개의 레코드를 넣고 시간을 비교해봤다. 루프문 명령어는 아래의 문서를 통해 알아냈다.
1회차
paging : 388
none-paging : 335
벌써부터 뭔가 싸늘하다.
2회차
paging : 385
none-paging : 347
알고보니 순서때문이 아닐까? 하는 생각에 순서를 바꿔서 진행해봤다.
3회차
none-paging : 358
paging : 395
4회차
none-paging : 323
paging : 349
더이상의 실험은 무의미했다. 페이징이 오히려 느리다.
여기서 얻을 수 있는 결과는
1. 페이징은 Full-Scan을 한 뒤 n개의 레코드만 잘라서 내보내준다. (H2-Console 환경이긴 하지만 MySQL도 다르진 않을 것이다.)
2. 게다가 추가적인 어떤 시간의 소모(아마도 정렬 알고리즘)가 있다.
결국 findAll()로 인해 너무 많은 데이터가 오고가는 그 부하를 제거할 수 있는 점을 제외하면 이득이 없는 기능인 것이다.
제대로 알지도 못 하고 그동안 이거 좋은듯 좋아라 하면서 사용했던 나는 반성을 해 본다.
생각해보면 Full-Scan 하는게 당연하다. 페이징에 기본적으로 달려있는 정렬 기능을 시행하려면 Full-Scan을 해야하는게 맞는 것이다. 조금만 생각해보면 되는 것을.. 허허
그러면 또 실험해보고싶은게 있다.
단순 데이터 두 번 불러오는게 그러면 더 빠르지 않을까?
Optional<User> found = userRepository.findByUsername(username);
Optional<User> found2 = userRepository.findByNickname(nickname);
이미 사용하던 코드가 있어서, 그냥 핸디캡 주는셈 치고 Optional도 씌워줬다.
그럼 시간측정 들어간다.
1회차
2 times call : 37
아니 그냥 2회차 비교가 필요가 없다.. 말 할 것도 없이 훨씬 빠르다.
이미 수량이 제한되어있는 데이터를 받아오는 상황에선 findBy를 2번 쓰는게 나은 것이다. 추측하건대 두 번째 데이터는 찾아오는데 걸리는 시간이 1이지 않았을까 싶다. 그래. 그런 것이다. 난 배웠다.
이번 실험으로 얻은 교훈은...
findAll 사용은 신중하게 하자!
'기술 > Spring-Boot' 카테고리의 다른 글
[SpringBoot/Redis] SpringBoot에서의 Redis 기본 명령어 (0) | 2021.12.29 |
---|---|
빌더패턴 제네릭 클래스에 적용하기 (1) | 2021.12.24 |
[QueryDSL/SpringBoot] Gradle에서의 QueryDSL 설정 (0) | 2021.12.10 |
JPA에서 쓰는 페이징(Paging) 기법이 뭘까? (0) | 2021.12.09 |
@Autowired는 왜 별로일까? (0) | 2021.11.28 |
댓글