최근 아는 개발자 분께 질문을 드리다 여러 데이터를 가져올 때는 where 문보다 join 문의 성능이 더 좋을 수 있다는 것을 알게 되었다.
기존 query
기존에 프로젝트를 진행하면서 특정 유저의 콘텐츠를 조회할 때,
해당 user와 content 테이블은 서로 1 : N의 관계로 연결되어있었기 때문에
user의 id를 where 조건문에 사용하여 content 테이블에 query를 날려 데이터를 조회하고 있었다.
코드의 일부분을 보여주자면 다음과 같다.
await this.contents
.createQueryBuilder('content')
.where('content.userId = :userId', { userId: user.id })
.leftJoinAndSelect('content.category', 'category')
.getMany();
이렇게 content 테이블에 where 문을 사용하여 조회하고 있었는데,
join 문을 사용하는 것이 성능이 더 좋다는 말을 듣게 된 것이다.
사실 join 문이 더 좋다는 말을 듣고 나선 content 테이블에 원하는 데이터가 전부 있고,
조건에 사용하는 userId의 무결성에도 문제가 없는데
왜 join 문을 사용하는게 더 성능이 좋은 것인가? 하는 의문이 들었었다.
(사실 join은 서로 다른 테이블들을 연결하기 때문에 성능이 더 좋지 않을 것이란 생각을 가지고 있었다.)
그러나 알고보니 단일 데이터 조회는 where 문의 성능이 더 좋을 수 있지만
n 개의 데이터를 조회할 때는 join 문의 성능이 더 좋으며,
(물론 '무조건 join이 좋다'는 아니지만 지금 나의 경우와 같다면 무조건 join을 사용하는게 낫다는 말씀까지 해주셨다.)
join 문의 성능이 더 좋은 이유
한 문장으로 정리하자면,
대부분의 RDB는 외래키 또는 기본키에 대해 인덱스를 생성하기 때문에
join 문을 사용하면 인덱스를 활용할 수 있어서 성능이 더 좋다.
물론 join의 경우에는 두 개 이상의 테이블에서 데이터를 가져와서 병합해야 해서 처리 비용이 더 들긴 하고,
where이 단순 쿼리면에서는 빠르지만
데이터의 개수가 n개이면 선형적으로 비용이 증가하기 때문에
인덱스를 활용하는 join이 처리 속도가 더 빠르다는 것이다.
의문
인덱스를 활용하기 때문에 효율이 더 좋다는 것은 알겠다.
그러나 RDB가 외래키에 대한 인덱스를 설정하게 될텐데
결국 where 문의 조건에 사용하는 값도 인덱싱이 되어있기 때문에 똑같이 성능이 좋은 것이 아닌가?
결론
물론 외래키에 대한 인덱스가 생성되어 where 문을 사용해도 인덱스를 사용하기 때문에
Case 1 : where
SELECT * FROM content WHERE userId = {유저 id};
와 같은 쿼리도 빨라지겠지만,
결국 where 문은 content 테이블의 모든 행을 탐색하여 userId 컬럼이 조건에 부합하는 행만 반환하기 때문에
이 쿼리는 userId 컬럼에 인덱스가 있더라도 모든 행을 스캔해야 하므로 비교적 성능이 떨어진다는 것이다.
Case 2 : join
SELECT *
FROM "user" "user"
LEFT JOIN "content" "content" ON "content"."userId"="user"."id"
WHERE "user"."id" = {유저 id};
반면에 join을 사용하여 user 테이블과 content 테이블을 조인하면, 데이터베이스는 user 테이블의 id가 조건에 부합하는 행만 선택하고, content 테이블을 불러오기 위해 추가적인 쿼리를 수행하지 않게 된다.
결국
DBMS 입장에서 더 효율적으로 인덱스를 활용할 수 있게 되는 join 문이 훨씬 빠른 것이다.
물론 외래키에 대한 index가 생성되지 않는 경우도 있기 때문에 제대로 성능을 개선하고자 한다면
index가 생성되었는지 확인하고, 없다면 생성한 후 작업해주어야할 것이다.
index 확인하기
psql에서 데이터베이스 객체의 구조를 확인할 수 있는 명령어인
\d
를 통해 content 테이블의 구조를 확인해보자.
위와 같이 정보들을 확인할 수 있으며,
하단에 index 정보도 함께 볼 수 있다.
사진의 정보를 통해 알 수 있듯이 외래키에 대한 index는 없는 것으로 판단된다.
SELECT EXISTS (
SELECT 1
FROM pg_indexes
WHERE tablename = 'content'
AND indexname = 'idx_content_userId'
);
위와 같은 쿼리를 통해 다시 확인해봐도
확실하게 없다는 것을 알 수 있다.
인덱싱 작업 후 쿼리를 개선하도록 해야겠다..
'Dev' 카테고리의 다른 글
[JS] Call by reference가 없는 Javascript (2) | 2023.04.23 |
---|---|
HA(고가용성)와 DR(재해 복구) (0) | 2022.11.19 |
Docker timezone 설정하기 (0) | 2022.10.19 |
github action을 통한 ci cd 자동화(feat. shell script) (0) | 2022.10.17 |
Intellij 단축키 정복하기 (0) | 2022.05.22 |