인프런 커뮤니티 질문&답변
rows per chunk 에 대해서 궁금합니다.
해결된 질문
작성
·
193
0
이번에 커버링 인덱스를 설명하시면서 아 대충 왜 빠르게 조회가 되는지 이해가 되기 시작했습니다.
그런데 최근에 했던 프로젝트 중에 이와 비슷한데 왜 성능이 개선되었는지 모르는 것이 하나 있습니다.
아래 부분인데요..
일단 created_at 은 index 가 적용되지 않았음을 알려드립니다.
select
ep1_0.employee_post_id,
ep1_0.access_url,
ep1_0.contact,
ep1_0.contents,
ep1_0.member_id,
m1_0.member_id,
m1_0.access_url,
m1_0.authority,
m1_0.birth_day,
m1_0.email,
m1_0.login_id,
m1_0.name,
m1_0.nick_name,
m1_0.password,
m1_0.personal_link,
m1_0.personal_statement,
m1_0.sex,
m1_0.tmp_password,
m1_0.twitter_link,
m1_0.youtube_link,
ep1_0.payment_amount,
ep1_0.payment_method,
ep1_0.title,
wft1_0.work_field_tag_id,
wft1_0.name,
ep1_0.career_year,
ep1_0.created_at,
ep1_0.updated_at
from
employee_post ep1_0
left join
work_field_tag wft1_0
on wft1_0.work_field_tag_id=ep1_0.work_field_tag_id
join
member m1_0
on m1_0.member_id=ep1_0.member_id
where
wft1_0.work_field_tag_id=1
order by
ep1_0.created_at desc,
ep1_0.employee_post_id desc
limit
0,10;이것을 explain analyze 한다면
Limit: 10 row(s) (cost=1088 rows=10) (actual time=39.1..39.1 rows=10 loops=1)
-> Nested loop inner join (cost=1088 rows=1968) (actual time=39.1..39.1 rows=10 loops=1)
-> Sort: ep1_0.created_at DESC, ep1_0.employee_post_id DESC (cost=399 rows=1968) (actual time=39..39 rows=10 loops=1)
-> Index lookup on ep1_0 using FK_work_field_tag_TO_employee_post (work_field_tag_id=1) (cost=399 rows=1968) (actual time=0.0949..7.91 rows=1968 loops=1)
-> Single-row index lookup on m1_0 using PRIMARY (member_id=ep1_0.member_id) (cost=0.25 rows=1) (actual time=0.011..0.011 rows=1 loops=10)이렇게 나오고 성능이 무척 안좋은 것을 볼 수 있었습니다(using filesort 가 직접적인 원인)
근데 문제는
member 에 대한 inner join 을 빼면
-> Limit: 10 row(s) (cost=398 rows=10) (actual time=10.5..10.5 rows=10 loops=1)
-> Sort: ep1_0.created_at DESC, ep1_0.employee_post_id DESC, limit input to 10 row(s) per chunk (cost=398 rows=1968) (actual time=10.5..10.5 rows=10 loops=1)
-> Index lookup on ep1_0 using FK_work_field_tag_TO_employee_post (work_field_tag_id=1) (cost=398 rows=1968) (actual time=0.0712..6.03 rows=1968 loops=1)
위와 같이 나오면서 성능이 무척 개선된다는 점입니다.
제가 보았을 떄 핵심적인 부분은 10 rows per chunk 입니다.
혹시 10 rows per chunk 같은 키워드는 언제 언제 발생하는지 알 수 있을까요??(왜 member에 대한 join 을 빼면 나타나구, wft1_0 을 left outer join 을 하는 것은 영향이 없는지..)
mysql 공식 문서를 계속 뒤져도 발견하지 못했습니다.
강의와 관련 없는 부분이라고도 생각하실 수 있는데 이런 질문 해서 정말 죄송합니다..
관련해서 블로그 정리한 거 혹시 필요한 정보가 있을 수도 있으니 링크 남겨놓겠습니다...
답변 3
0
아래 답변에 댓글 남겨주신 것 보고, 추가 답변 드립니다!
이론적인 부분은 당연히 공식문서나 이미 다 학습해 둔 인공지능을 따라갈 수는 없고요.
요즘 인공지능으로 깊이 파고 들어가는게 쉽다보니, 인공지능이 명쾌한 답변을 못 내는 지점부터 의문이 점점 깊어지는 것 같기도 하군요..!
사실 이러한 부분은 저도 정말 딥하게 파거나 직접 까봐야 하기 때문에 100% 구체적으로 또는 명쾌하게 답변 드리기는 어렵습니다.
전 데이터베이스 개발자도 아니고, 그 정도로 시간을 투자할 필요도 없다고 생각하고요. (그 이유도 아래에서 언급드려 봅니다.)
그래서 저도 아래 답변은 어디서 찾아본건 아니고(직접 까보지 않는 이상 공식적으로 찾을 수 있는지도 모르겠고), 알고 있는 지식 기반으로 유추해 봅니다.
member와의 join된 플랜을 보면, 정렬 전에 inner join을 먼저 수행합니다.
조인된 데이터 집합이 employee_post와 member 데이터 모두를 포함하고 있기 때문에 데이터는 더욱 커집니다.
데이터가 커지면 메모리에서 정렬하지 못하는 것은 당연합니다.
메모리의 크기는 디스크보다 작고 제한적이기 때문에, 메모리에 정렬이 필요한 모든 데이터를 한번에 불러올 수 없습니다.
디스크를 활용하여 정렬해야 하므로 디스크 I/O 비용은 불가피합니다.
(정확히는, 데이터 크기에 따라 반드시 디스크 I/O가 필요한건 아니지만 아무튼 filesort는 느리다는 관점만 이해하시면 될 것 같습니다.)
따라서, 데이터가 크기 때문에 filesort로 정렬된다는 것이므로 느린 이유는 이해될 것 같습니다.
member join이 없는 경우, 옵티마이저에 의해 정렬을 먼저 수행하는 것을 볼 수 있습니다.
이번에는 member 데이터가 없는 employee_post 데이터만 정렬하기 때문에,
"현 시점"에는 filesort를 안해도 된다고 판단할 수 있습니다.
member join이 있을 때보다 정렬해야 할 데이터 크기가 작아졌기 때문입니다.
아무튼 데이터 크기가 작아졌기 때문에, 옵티마이저가 더 빠르게 최적화할 방법이 찾아준 것이고,
그로 인해 10 rows per chunk라고 나온 것 같네요.
10 rows per chunk에 대해 조금 더 들어가기 전에, 다음과 같은 알고리즘 문제를 풀어보겠습니다.
“N건의 데이터에서 상위 K건을 찾는 방법”이라는 문제인데요,
단순하게 모든 데이터 정렬하고, 앞에서 K건을 뽑아내면 되는 것 아냐? 라고 생각하실 수 있습니다.
그렇게 되면 모든 데이터를 정렬해야 하므로 시간복잡도는 O(NlogN)이 됩니다.
(정렬 알고리즘에 따라 O(N) 또는 O(N^2) 등 다양할 수 있지만, 여기서는 일반적인 퀵 또는 병합 정렬 같이 선형로그시간으로 가정합니다.)
그런데, 상위 K건만 찾는건 반드시 모든 데이터를 정렬할 필요는 없습니다.
힙(우선순위큐)을 이용하면, O(NlogK) 시간으로 풀 수 있습니다.
N건의 데이터를 순회하며 힙에는 상위 K건의 데이터만 유지한다면,
힙에 데이터를 삽입/삭제하는 시간은 O(logK)이고, 이를 N번 수행하게 됩니다.
그렇기 때문에 O(NlogK)의 시간으로 풀 수 있는 것입니다.
10 rows per chunk로 돌아가보겠습니다.
order by + limit 조합이기 때문에 위 언급된 문제와 동일합니다.
Limit = K = 10이기 때문에 상위 10건만 찾으면 됩니다.
힙에 10개의 데이터만 유지하고, N건의 데이터를 순회하면 됩니다. O(NlogK)에 풀 수 있는 문제입니다.
그런데 member join이 없을 때에만 왜 10 rows per chunk가 떴는가에 대해 궁금할 수 있습니다.
이 부분은 옵티마이저에 의한 판단이 들어갈 것 같습니다.
단순하게 시간복잡도만 고려하기에는, 데이터 크기에 따라 메모리 뿐만 아니라 디스크 I/O가 필요한 시점이 생기거든요.
이제는 I/O 비용도 같이 고려가 필요한 겁니다.
위에 언급되었듯이 member join을 수행하면 정렬해야 할 데이터의 크기가 커지고, member join을 수행하지 않으면 정렬해야 할 데이터의 크기가 작습니다.
옵티마이저는 이 크기에 대한 기준을 가지고, 어떤 방법을 할지 결정하게 됩니다.
구체적인 기준은 저도 모르지만(정말 찾으려면 찾을 수 있겠지만 굳이?),
아무튼 옵티마이저가 내부적인 통계 정보나 최적화 등을 고려해서 O(NlogK)의 시간으로 푸는게 가능하다고 판단한 것 같습니다.
그렇기 때문에 10개의 데이터만 메모리에 유지해둘 수 있습니다.
아무튼 K는 메모리에 올라와있기 충분한 크기일 것이고,
N은 메모리에 한번에 올릴 수 있든지 디스크에서 순차적으로 불러오든지 하겠고요.
10개만 불러오는게 아니라, 정렬 데이터는 10개만 유지할 수 있다는 것입니다.
정리하면, order by + limit + 옵티마이저가 데이터 크기가 최적화를 적용하기에 충분하다고 판단했을 때,
해당 기법을 사용하게 되는 것 같습니다.
어떤 기법을 사용하는지는, 알고리즘의 시간복잡도 뿐만 아니라 데이터 크기, 개수, I/O 비용 등 다양한 요소들을 모두 고려하게 되는 것이고요.
길어서 내용이 짤리네요. 댓글로 이어서..
일단 이렇게 까지 답변해주시는 것 정말정말 감사합니다. 다듣고 수강평도 잘 쓰겠습니당.ㅎㅎ
그런데
"member와의 join된 플랜을 보면, 정렬 전에 inner join을 먼저 수행합니다."
라고 하셨는데
제가 알기로 들여쓰기가 안 쪽부터 먼저 수행된다고 알고 있습니다.
그렇다면 아래와 같은 문제쿼리는
work_field_tag_id 가 1인 것 싹 가져옴
이후 정렬
이후 join
이후 10개 행 반환
-> Limit: 10 row(s) (cost=1088 rows=10) (actual time=39.1..39.1 rows=10 loops=1)
-> Nested loop inner join (cost=1088 rows=1968) (actual time=39.1..39.1 rows=10 loops=1)
-> Sort: ep1_0.created_at DESC, ep1_0.employee_post_id DESC (cost=399 rows=1968) (actual time=39..39 rows=10 loops=1)
-> Index lookup on ep1_0 using FK_work_field_tag_TO_employee_post (work_field_tag_id=1) (cost=399 rows=1968) (actual time=0.0949..7.91 rows=1968 loops=1)
-> Single-row index lookup on m1_0 using PRIMARY (member_id=ep1_0.member_id) (cost=0.25 rows=1) (actual time=0.011..0.011 rows=1 loops=10)
이렇게 되는 것인 줄 이해하고 있었는데 잘 못 이해하고 있던 것일까요??
그리고 저도 더 이상 깊이는 가지 않으려구 합니당.
사실 이제 졸업하는 취업 준비생 입장에서
굉장히 많은 취준생 속에서
누구나 다 cicd 를 하고 , 누구나 다 채팅 기능을 섞고, 누구나 다 CRUD 베이스 프로젝트를 하고 , 누구나 다 성능 개선을 하는 마당에
어떻게 해야
차별화될 수 있을까 를 생각하다가 이게 유일한 방법이라고 생각해서 하던 것이였습니당. ㅎㅎ
사람들이 실제로 사용하는 실사용 서비스를 만들고 싶은데 이것도 어느정도 운이 필요하다고 생객하기도 하구...
잡소리가 길었네요. 다시 한번 감사합니당.
앗 안쪽부터 였나요?
터미널에서 쿼리 실행 분석 오랜만에 보다보니 요건 제가 순서를 반대로 인지했네요.. ㅎㅎㅎ
말씀주신 순서가 맞는 것 같습니다.
아무튼 O(NlogK)로 동작할 수 있는 원리와 옵티마이저의 내부적인 판단,
그리고 세부사항까지는 몰라도 된다라는 관점에 대해서만 이해해주시면 될 것 같습니다!
옵티마이저 비용 계산은 꼭 순차적인 순서에 의해서 기준이 결정되는게 아니라, 이후에 연계되는 작업까지 고려하게 되거든요.
(세세한 이유까진 저도 잘 모르겠네요 ㅋㅋ 나중에 다시 고민해보고 짐작되는 사항 있으면 추가로 남겨보겠습니다)
요즘 상향평준화가 너무 많이 되긴 했습니다..
항상 기본기를 강조하지만, 마냥 기본기만으로는 취업이 안되는 시기기도 하고요.. ㅠㅠ
주변에 취준생 분들이나 신입 보면 다들 정말 잘하시는데.. 취업이 어렵다는걸 저도 많이 느끼네요..
그래도 꾸준히 잘 하고 계신 것 같으니 좋은 결과 있으실거라 기대합니다!!
화이팅입니다!!
아~ member join이 inner join이기 때문에 그런가보네요.
없는 데이터는 필터링을 해야하기 때문에, 정렬로 상위 N건만 남길 수가 없습니다.
모든 데이터 다 정렬해서 가지고 있어야, member가 있는 유효한 데이터에 대해서만 결과로 만들어낼 수 있습니다.
그래서 당연히 모든 데이터가 다 필요하기 때문에, 전체 정렬을 수행한거네요.
어떻게 동작할지 직접 그려보면서 연구해보시면 느낌이 오실 것 같습니다.
정렬 결과에서 상위 N건만 보면 되는게 아니라, 없는 데이터는 필터링하면서 다 봐야할 수도 있으니깐요.
inner join으로 하면, 인덱스 잘 걸려있더라도 아마 인덱스 제대로 못 타고 느릴 것 같네요.
이런 경우 그냥 left join으로 처리하긴 합니다.
삭제된 member가 얼마나 될지 모르는데, 삭제된 member의 데이터를 판별하고 필터링하는 과정은 병목 지점이 되거든요.
member join을 left join으로 바꿔보면, 더 빠르게 수행될 것 같긴 하네요.
(명확한 사항인데 제가 실행 순서를 반대로 보고 있어서 헷갈렸네요..;)
0
dionisos198님, 안녕하세요!
일단 10 rows per chunk가 주요한 부분은 아닐 것 같네요,
정렬을 "그나마" 빠르게 처리하기 위해 저 동작이 수행되는 것 같습니다.
하지만 여전히 느린 쿼리로 보입니다. (데이터가 많아질수록)
애초에 정렬을 조회 시점에 처리하는게 아니라,
정렬 상태의 인덱스를 즉시 태울 수 있어야 합니다!
조회 시점에 정렬을 하고 있어서 느린 것 같네요.
혹시 employee_post 테이블에 인덱스가 어떻게 걸려있나요?
인덱스가 (work_field_tag_id) 1개의 컬럼에 대해서만 걸려있는게 맞을까요?
work_field_tag_id 인덱스로 필터링 결과를 가져오고, 필터링 결과에 대해서 조회 시점에 정렬을 하기 때문에 느린걸로 보이네요!
wft1_0.work_field_tag_id=1 조건 -> ep1_0.work_field_tag_id=1로 바꿔주고(어차피 조인키라 상관없을 것 같긴한데 정확히 명시),
employee_post의 인덱스를,
(work_field_tag_id asc, created_at desc, employee_post_id desc)로 만들어두면, 빠르게 처리될 것 같네요!?
위처럼 해두면, 조회 시점에 정렬을 하는게 아니라,
인덱스 자체가 이미 정렬된 상태로 만들어져있기 때문에 인덱스를 즉시 스캔하게 됩니다!
인덱스 구조(B+ Tree)와 동작 원리(클러스터드/세컨더리)를 이해하고, 천천히 흐름을 따라가보면 왜 느린지 이해가 될 것 같습니다!
혹시 더 궁금한 점 있으시면 편히 문의 주세요!
블로그 남겨주신건 상세히 본건 아니고 슬쩍 보았는데요, 몇 가지 첨언으로 남겨봅니다!
(슥 내리면서 눈에 띈 것만 본거라, 이미 알고 계신 부분일 수도 있습니다..! 꼼꼼하게 분석도 많이 하시고, 고민도 많이 하셨네요 ㅎㅎㅎ 멋진 자세입니다.)
한방 쿼리가 반드시 좋은건 아닙니다.
쿼리를 나눠서 전송하면, 여러번 쿼리를 나눠서 전송해야하는 비용이 있지만,
DB가 한번에 처리해야하는 부담이 줄어듭니다.
또, 중간에 일부 쿼리는 DB에 안보내고 캐시로 질의할 수도 있습니다.
DB의 부하분산, 무거운 한방 질의로 인한 개별 응답 지연, 병목 등을 고려한다면, 가벼운 쿼리를 나눠서 전송하는게 유리할 수도 있습니다.
각 쿼리가 길고 복잡해지면, 쿼리를 이해하거나 최적화하는게 어려워지기도 하고요.
조회 조건이 다양하다면, 각 조건마다 인덱스는 필요합니다.
물론, 컴포짓 인덱스로 조건/정렬이 유사한 쿼리에 대해서는 동일 인덱스를 활용할 수는 있겠지만,
데이터가 많아질수록 스캔해야하는 범위가 커질 수 있으므로, 점점 부담이 커지게 됩니다.
DB에 인덱스를 여러 개 만드는 것이 부담된다면, 관점 자체를 달리할 수도 있습니다.
인덱스는 개념인 것이지, DB 내장 기능(CREATE INDEX)으로만 만들 수 있는건 아닙니다.
다른 DB에 인덱스 테이블을 별도로 만들어서 인덱스 관리/조회 부하를 분산할 수도 있습니다.
별도로 만들어진 인덱스 데이터는 관계형 데이터베이스일 수도 있고, 아예 다른 종류의 데이터베이스일 수도 있습니다. (읽기/쓰기 트래픽 패턴에 따라 자료구조나 DB 종류도 선택적으로 가능)
마지막 조회 최적화 전략 강의에서는, 이러한 인덱스를 레디스에 직접 구현해보기도 합니다.
먼저 다양한 시각을 제시해 주셔서 감사합니다.
하지만 제 질문에 대한 궁금증은 아직 남아있습니다.
말씀드린 대로
employee_post 테이블에 member table 을 inner join 하였을 때
정렬시 index 가 적용되지 않기에 , using filesort 즉 메모리에서 정렬을 수행하는데
where 절에서 걸러지는 행이 2000 개라 아 2000 개를 메모리에서 정렬(size 가 sort_buffer_size 보다 커지면 합병 정렬까지 수행하기도) 하기 때문에
부하 상황 시 느려지는 것은 당연하겠구나 생각하였습니다.
실제로 show session status 쿼리를 통해 2000 개의 정렬횟수가 수행됨을 알수 있었습니다.
근데 똑같은 부분에서 member 테이블에 대한 조인을 빼면
10 rows per chunk 라는 키워드가 붙으면서
show session status 쿼리를 통해 10개만 정렬이 됨을 확인했습니다.(쿼리 실행 전 sort_rows 수가 10개 차이가 남)
실제로 부하 테스트 결과 성능이 엄청 개선되기도 하구요...
그래서 저는 이 rows per chunk 키워드가 굉장히 의심스럽습니다...
하지만 mysql 공식 문서에서 이 키워드를 검색해도 제가 부족한지 도저히 찾을 수가 없고,
자료가 잘 보이지 않아서 왠지 이 부분에 대해 잘 아실 것 같아서 여쭤본 겁니당...
참고로 rds 메모리는 최소입니다. (그래서 local 에서 돌리면 성능 차이가 없어용..
메모리가 1g엿던걸로 기억합니당.)
클로드를 쥐어 짰을 때는
2000 개의 행을 200개의 크기를 가진 10개의 chunk 로 나누고 각 chunk 마다 정렬 조건에 부합하는 10개를 priority_queue 를 통해 추출하고 그 정보만 메모리에 올려둔다. 라는 식으로 말하는데..
믿을 수가 없네염..
강의에서 나온 직접적인 부분은 아니지만...
다시 한번 질문 받아주셔서 넘넘 감사합니다.
using filesort
위 내용은 데이터 크기에 따라 메모리에서만 정렬할 때도 있지만, 디스크를 활용해서 정렬할 때도 있습니다.
DB의 데이터 크기는 웬만해선 많으므로, 그냥 디스크 활용해서 정렬한다고 보시는게 이해가 편합니다.
디스크 I/O가 있기 때문에 filesort는 느린 것이고요.
추가 문의에 대한 내용은, 새로운 답변으로 달아두었습니다!
2000 개의 행을 200개의 크기를 가진 10개의 chunk 로 나누고 각 chunk 마다 정렬 조건에 부합하는 10개를 priority_queue 를 통해 추출하고 그 정보만 메모리에 올려둔다. 라는 식으로 말하는데..
“N건의 데이터에서 상위 K건을 찾는 방법”이라는 문제를,
O(NlogN)이 아니라, O(NlogK) 시간에 풀어내는 기법인 것 같네요.
클로드가 알고리즘은 얼추 맞는 것 같으면서도 잘못 설명한 부분이 있어보이네요.
"정렬 조건에 부합하는 10개를 priority_queue 를 통해 추출하고 그 정보만 메모리에 올려둔다" 이 부분은 맞는 것 같습니다.
위 문제는 정렬 데이터는 10건만 유지할 수 있습니다. 10건만 정렬한다는게 그 의미인 것 같네요. 나머지 N은 그냥 순회하면 됩니다.
DB에서의 실제 내부 구현은 저도 모르고(알 필요도 없고), 문제 해결 방식에 적절한 알고리즘을 떠올리면 이런 식으로 동작이 가능합니다.
아마 알고리즘은 동일할 것 같고, DB 내부적으로 부가적인 나름의 최적화 기법은 있을 것 같네요. (I/O까지 고려해야하므로)
위 답변에 정리해두었고, 알고리즘에 대한 이해도 필요하네요.
0
안녕하세요, 인프런 AI 인턴입니다. dionisos198님.
질문해 주신 내용을 바탕으로, “10 rows per chunk”라는 키워드가 실행 계획에 나타나는 이유와 관련해 설명드리겠습니다. 이 현상은 MySQL의 내부 성능 최적화 방법과 관련이 있습니다.
10 rows per chunk의 의미
“10 rows per chunk” 키워드는 MySQL이 정렬을 수행하는 과정에서 내부적으로 사용되는 최적화 기법 중 하나를 나타냅니다. 이는 데이터를 정렬할 때 일정한 크기의 “청크” 단위로 작업하여 메모리 사용 및 성능을 최적화하는 방법입니다. 이 기법은 특히 큰 데이터 셋을 처리할 때 발생할 수 있습니다.
왜 성능에 영향이 있었을까요?
Join의 영향:
member테이블과의 조인(inner join)은 각 레코드에 대해 추가적인 데이터 조회를 유발하며, 이는 정렬 작업에 추가적인 부담을 줍니다. 조인이 내부적으로 복잡한 구조의 테이블일 때 성능이 저하될 수 있습니다.Join 제거 후: 조인을 제거하면 MySQL이 데이터를 처리하는 데 있어서 더 적은 리소스를 사용하게 됩니다. 이로 인해 정렬 및 제한 작업이 더 효율적으로 수행될 수 있습니다.
정렬과 청크: MySQL은 정렬할 때 데이터 청크를 사용하여 메모리 공간을 절약하며, 이는 특정 상황에서 성능을 크게 향상시킬 수 있습니다. 조인이 없을 때 MySQL이 더 효율적으로 정렬 작업을 수행할 수 있는 기능을 사용할 수 있게 됩니다.
추가 자료
추가적인 성능 최적화 및 쿼리 실행 계획에 대한 정보를 얻으시려면 아래 링크를 참고하시기 바랍니다:
이상으로 성능 차이에 대해 설명드렸습니다. 이러한 차이를 고려하여 쿼리를 설계할 때, 특히 조인과 같은 복잡한 연산을 효율적으로 최적화하는 것이 중요합니다.
저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.






애초에 현 쿼리가 제대로된 최적화가 일어나지 않았다는 점은,
B+ Tree라는 자료구조 / 인덱스의 동작 원리를 알고 있으면 이해해볼 수 있습니다.
여기서 핵심은, 최적화를 제대로 하는게 중요한데,
지금 빨라진 것처럼 보인다고 해서, 그게 정말 최적화되었다고 보기에는 어려움이 있습니다.
지금 member join이 없을때 빨라진 것은, 그냥 “지금 데이터 크기가 작아서”, 당장은 "그나마" 빨라진 것입니다.
아마 조인을 지금처럼 안하더라도, 데이터가 더욱 많아지면 동일한 쿼리더라도 filesort로 바뀌고, 느려지는 시점은 오게 됩니다.
옵티마이저가 해당 최적화 기법을 사용할 수 없다고 판단하는 시점이 될 것 같네요.
대부분의 최적화 관점에서는 다음 위 3가지 관점 정도만 이해하시면 됩니다. (더 필요한 순간도 있겠지만요)
1. 메모리에서 정렬을 했는가
2. 디스크에서 정렬을 했는가
3. 정렬된 인덱스를 이용했는가
위 범주 내에서,
Order by + limit + 데이터 크기가 옵티마이저에 의해 작다고 판단되었을 때,
청크 단위(limit)의 데이터 상위 K건만 메모리에 유지하는 최적화 기법이 부가적으로 있던 것이고요.
조회 시점에 정렬을 수행하는건 데이터가 많아질수록 느려질 수 밖에 없습니다.
아무튼 지금 인덱스를 못타고 있는 상황이란건 명확한 것이고요,
추후에도 병목없이 관리하려면 인덱스를 미리 제대로 태우도록 최적화해두는게 편할 것 같네요.
데이터가 더 늘어나지 않을거라면, 지금 상태를 유지하거나 중간에 캐시를 붙이는걸로 충분할 수도 있고요.
이거는 구체적인 사정까지는 제가 다 알 수는 없다보니, 현 상황 잘 고려해서 판단하시면 좋을 것 같습니다.
요즘에는 디스크 비용도 저렴하고, 컴퓨터 성능도 정말 좋고, 데이터베이스도 정말 잘 만들어져 있습니다.
인덱스 하나 만드는 비용 아끼려고 명확한 병목 지점을 만드는 것 보단, 그냥 약간의 비용 더 들여서 병목 지점을 명확하게 해소하는 것도 좋습니다.
자칫 신경 못쓰고 있다가는 시스템 전체 장애로 전파될 수도 있는 지점이고, 그렇게 되면 서비스 가용성이나 안정성, 신뢰성은 떨어지게 되겠죠.
그게 더 큰 문제일 것 같네요.
또 한가지로, 추상화에 대해서도 언급드리고 싶은데요,
객체지향 공부하면서 추상화란 개념도 많이 학습하셨을 것 같습니다.
“내부 구현을 몰라도 되도록” 추상화를 적용하고, 네이밍도 잘 짓고, 딱 봐도 알기 쉽게 만드려고 합니다.
당연히 DB의 최적화를 위해서는 내부 구현도 어느정도 알아야 하지만, 세부 구현에 대해서는 어느정도 끊어내는 선이 필요할 때도 있습니다.
제가 처음 답변 드렸던 내용에서, 10 rows per chunk가 주요한 부분은 아닐 것 같다고 한 것이 이러한 의미입니다.
“이러한 최적화 기법이 있었고, 이런 식으로 동작하겠구나” 정도만 알면 충분할 때가 많습니다.
통계 데이터 까보고, 데이터 개수/크기 변화하면서 테스트 하다보면, 더욱 구체화되는 시점이 오겠지만,
이런 과정이 정말 시간 투자대비 유의미하고 필요할건지 고민해보는 것도 좋습니다.
사람들의 편의를 위해서 프로그램을 만드는 것인지, 기술을 위해서 프로그램을 만드는건 아니니깐요.
물론, 깊게 파보는 과정이 절대 나쁘단 의미는 아닙니다!
해당 내용은 알고리즘에 대한 지식이 있으면 어느 정도 유추 가능한 부분이 될 수도 있어서, 기본기에 대한 중요성도 강조해봅니다!
(사람마다 생각은 다를 수 있으므로, 적당히 걸러서 받아들이시면 됩니다.)
충분한 답변이 되었을지는 모르겠지만..
더 궁금한 점 있으시면 말씀 주셔요!