그룹 프로젝트에서 트위터와 유사한 SNS를 개발하고 있습니다.
백엔드는 Nest.js + Mongoose(MongoDB) 로 진행중입니다.

유저 프로필 페이지에서 게시글, 팔로워, 팔로잉 수를 반환해야 하는데, 이를 어떻게 구현해야 할지 고민입니다.
현행
@Schema({ versionKey: false, timestamps: true }) export class User { ... @Prop({ default: 0, }) postcount: number; @Prop({ default: 0, }) follower: number; @Prop({ default: 0, }) following: number; ... }
유저 스키마에 카운트를 두고 있습니다.
async createPost(createBoardDto: CreatePostDto, user: User): Promise<Post> { const post = await this.postRepository.create(createBoardDto, user); this.userRepository.updatePostCount({ userid: user.userid }, 1); return post; }
async updatePostCount(userFilterQuery: FilterQuery<User>, postCount: number) { const result = this.userModel.findOneAndUpdate( userFilterQuery, { $inc: { postcount: postCount }, }, { new: true }, ); if (!result) throw new NotFoundException(); return result; }
위와 같이 게시글의 수가 증감하는 상황에 유저 카운트를 1씩 증가/감소 시키는 방향으로 하고 있습니다.
이렇게 구현한 이유
- 빠릅니다.
현행의 문제
의도치 않게 카운트가 어긋나는 경우가 생깁니다. 예를 들어,
- 게시글 추가/삭제가 되지 않았는데도 카운트가 증감되거나
- 카운트를 1 증감시키는 액션이 의도치 않게 취소되거나
- 테스트를 위해 DB 상에 직접 추가하는 경우
고민중인 다른 대안
- 카운트 수를 요청할 때마다 계산
- MongoDB에서는 count, countDocuments 를 제공합니다.
- 현재 50만 개 정도의 포스트가 있고, 작성자 기준으로 인덱싱했음에도 불구하고 카운트를 세는 것이 2~300ms 정도로 오래 걸립니다.
- MongoDB의 B+ tree indexing에 count 정보를 포함하지 않기 때문에 인덱싱 탐색을 하더라도 leaf 하나 하나 다 확인하기 때문인 것으로 파악이 됩니다. (MySQL은 ISAM에서만 인덱싱을 통한 O(1) 카운트를 지원하고, InnoDB는 마찬가지로 O(n) 인것으로 파악됨)
- 게시글 수가 100개 미만인 유저의 게시글 카운트는 1ms 안팎이지만 게시글 50만개를 작성한 유저 기준으로 2~300ms가 소요됩니다.
- 프로필을 조회할 때마다 DB를 300ms 를 쓰는 것은 너무 부담됩니다.
- 현재 50만 개 정도의 포스트가 있고, 작성자 기준으로 인덱싱했음에도 불구하고 카운트를 세는 것이 2~300ms 정도로 오래 걸립니다.
- MongoDB의 B+ tree indexing에 count 정보를 포함하지 않기 때문에 인덱싱 탐색을 하더라도 leaf 하나 하나 다 확인하기 때문인 것으로 파악이 됩니다. (MySQL은 ISAM에서만 인덱싱을 통한 O(1) 카운트를 지원하고, InnoDB는 마찬가지로 O(n) 인것으로 파악됨)
- 게시글 수가 100개 미만인 유저의 게시글 카운트는 1ms 안팎이지만 게시글 50만개를 작성한 유저 기준으로 2~300ms가 소요됩니다.
- 프로필을 조회할 때마다 DB를 300ms 를 쓰는 것은 너무 부담됩니다.
- 게시글 추가/삭제를 할 때마다 카운트 수를 계산하여 유저 rows에 보관
- 현행과 마찬가지로 카운트 업데이트 도중 취소되는 것에 어떻게 대처해야 할지 모르겠습니다. → 트랜잭션
- 게시글을 쓰고 지우거나, 팔로잉을 추가하고 취소할 때마다 인덱스 크기가 50만 기준으로 2~300ms가 소요될 것입니다.
async createPost(createBoardDto: CreatePostDto, user: User): Promise<Post> { const post = await this.postRepository.create(createBoardDto, user); this.userRepository.updatePostCount({ userid: user.userid }); //여기서 업데이트를 한다면? return post; }
어떻게 해야 DB에 부담이 덜 가면서 빠르게 게시글/팔로워/팔로잉 카운트를 반환할 수 있을까요?
CQRS?
redis + transaction