Skip to content

다른 사용자 정보 조회

박찬얼 edited this page Sep 16, 2023 · 9 revisions

제목

다른 유저 프로필 조회 API 구현 방식에 따른 속도 테스트

이슈에 대한 설명

유저 프로필 조회 API 를 2가지 방식으로 구현했습니다.

시드데이터

스크린샷 2021-12-09 오후 9 05 20 스크린샷 2021-12-09 오후 9 07 27

user 1 은 질문 4000개 답변 2000개 댓글 1만개 고인물입니다.

스크린샷 2021-12-09 오후 9 31 31

user 2 는 질문 2개 답변 10개 댓글 15개 뉴비입니다.

스크린샷 2021-12-09 오후 9 31 48

1. 첫번째 시도한 방법

답변,질문,유저 테이블을 조인해서 데이터를 가져온 후 length로 갯수를 체크한다음 리턴 -> 쿼리를 한번날림

	async getUserProfile(userId: number) {
		const user = await this.userRepository
			.createQueryBuilder('user')
			.innerJoinAndSelect('user.question', 'question')
			.innerJoinAndSelect('user.answer', 'answer')
			.innerJoinAndSelect('user.comment', 'comment')
			.where('user.id = :userId', { userId })
			.getOne();

		const userProfile = {
			user: user,
			questionCount: user.question.length,
			answerCount: user.answer.length,
			commentCount: user.comment.length
		};
		return userProfile;
	}

User1(고인물)

스크린샷 2021-12-09 오후 9 24 23 스크린샷 2021-12-09 오후 9 21 58 스크린샷 2021-12-09 오후 9 21 48

응답이 늦어 결국 서버가 죽었습니다.

select 의 문제인가 싶어 innerjoinandselect 대신 select에 조인한 데이터의 id만 명시해줬습니다만 결과는 같았습니다.

밑의 코드 참고

async getUserProfile(userId: number) {
		const user = await this.userRepository
			.createQueryBuilder('user')
			.innerJoin('user.question', 'question')
			.innerJoin('user.answer', 'answer')
			.innerJoin('user.comment', 'comment')
			.where('user.id = :userId', { userId })
			.select(['user.id', 'user.nickname', 'user.email', 'user.photo', 'user.liked_count', 'question.id', 'answer.id', 'comment.id'])
			.getOne();

		const userProfile = {
			user: user,
			questionCount: user.question.length,
			answerCount: user.answer.length,
			commentCount: user.comment.length
		};
		return userProfile;
	}

User2(뉴비)

20~30ms

스크린샷 2021-12-09 오후 9 22 36

2.두번째 시도한 방법

참고링크

loadRelationCountAndMap을 사용해서 각 갯수를 가져옴 실제로 실행되는 쿼리

쿼리가 3개 날아감

	async getUserProfile(userId: number) {
		const user = await this.userRepository
			.createQueryBuilder('user')
			.where('user.id = :userId', { userId })
			.loadRelationCountAndMap('user.questionCount', 'user.question')
			.loadRelationCountAndMap('user.answerCount', 'user.answer')
			.loadRelationCountAndMap('user.commentCount', 'user.comment')
			.select(['user.id', 'user.nickname', 'user.email', 'user.photo', 'user.liked_count'])
			.getOne();

		return user;
	}

User1(고인물)

20~30ms

스크린샷 2021-12-09 오후 9 12 37

User2(뉴비)

18~25ms

스크린샷 2021-12-09 오후 9 13 01

결론

예상했던 결과

쿼리를 여러번 날리는 B가 더 느릴 것이라 예상

실제 결과

많은 데이터에서는 B가 비교 불가능한 수준으로 빨랐고, 적은 데이터에서 또한 B가 빨랐습니다.

빠른 이유

복잡한 JOIN 대신 loadRelationCountAndMap 사용

두 번째 코드에서는 loadRelationCountAndMap 메서드를 사용하여 관련 엔티티의 개수를 직접 로드하고 매핑합니다. 이렇게 하면 복잡한 JOIN 문을 사용하지 않아도 됩니다. JOIN은 데이터베이스 성능에 영향을 미칠 수 있는 높은 비용을 가질 수 있으므로, 가능하면 사용을 최소화하는 것이 좋습니다.

데이터 로드 최적화

두 번째 코드에서는 loadRelationCountAndMap 메서드를 사용하여 필요한 데이터만 로드합니다. 이로 인해 불필요한 데이터를 가져오지 않아 네트워크 대역폭과 메모리 사용을 절약할 수 있습니다.

성능 개선

두 번째 코드는 데이터베이스 성능을 개선합니다. 불필요한 JOIN을 제거하고 관련 데이터의 개수만을 가져오므로 빠른 응답 시간을 기대할 수 있습니다.