본문 바로가기
NestJS

[NestJS] DB 인덱싱

by Programmer.Junny 2025. 5. 21.

1. 인덱스란?

책의 목차처럼, 테이블의 특정 컬럼 값과 그 값이 저장된 물리적 위치(레코드의 주소)를 매핑한 자료구조이다.

인덱스를 통해 테이블 전체를 순차 검색(Full Table Scan)하는 대신, 필요한 레코드의 위치를 빠르게 찾을 수 있어 조회 성능이 크게 향상된다.

2. 인덱스 하기 전

5만건의 데이터를 넣은 후(authorId가 3), 약 20건의 post만 authorId가 2인 사용자로 지정했다면,

author id가 2인 사용자를 찾기

Postman으로 요청 시 약 170ms 가 걸리는 것을 볼 수 있다.

DB에서 좀 더 자세히 분석을 해보면, Parallel Seq Scan on posts_model 이라고 뜨는데 이것은 posts_model을 병렬 스캔을 통해 authorId가 2인 사용자를 찾는다는 뜻이다. (요약하자면 오래걸린다는 뜻)

Execution Time이 121.665 ms 가 나온다.

3. 인덱싱

import { IsString } from 'class-validator';
import { BaseModel } from 'src/common/entity/base.entity';
import { ImageModel } from 'src/common/entity/image.entity';
import { stringValidationMessage } from 'src/common/validation-message/string-validation.message';
import { UsersModel } from 'src/users/entity/users.entity';
import { Column, Entity, Index, JoinColumn, ManyToOne, OneToMany, RelationId } from 'typeorm';
import { CommentsModel } from '../comments/entity/comments.entity';

@Entity()
export class PostsModel extends BaseModel {
  @ManyToOne(() => UsersModel, (user) => user.posts, {
    nullable: false,
  })
  @JoinColumn( {name: 'authorId'})
  author: UsersModel;

  @Column()
  @Index('idx_post_author_id')
  @RelationId((post: PostsModel) => post.author)
  authorId: number;

  @Column()
  @IsString({
    message: stringValidationMessage,
  })
  title: string;

  @Column()
  @IsString({
    message: stringValidationMessage,
  })
  content: string;

  @Column({
    default: 0,
  })
  likeCount: number;

  @Column({
    default: 0,
  })
  commentCount: number;

  @OneToMany(() => ImageModel, (image) => image.post)
  images: ImageModel[];

  @OneToMany(() => CommentsModel, (comment) => comment.post)
  comments: CommentsModel[];
}
@Column()
@Index('idx_post_author_id')
@RelationId((post: PostsModel) => post.author)
authorId: number;

기존에 없던 authorId를 추가하였다. 

데코레이터는 @Index(키), @RelationId((엔티티) => 엔티티의 컬럼)

이렇게하면 authorIdPostsModelauthor와 연결되어 인덱싱이 생성된다.

// synchronize: true,
synchronize: configService.get<boolean>('db.synchronize', true),

app.module에서 DB와 synchronizetrue가 되어있어야 서버를 재시작한 경우 인덱싱이 진행된다.

npm run typeorm migration:generate -- -n UpdatePostsIndexes
npm run typeorm migration:run

혹은 위와 같은 명령어로 인덱싱을 진행할 수 있다.

SELECT indexname, indexdef FROM pg_indexes WHERE tablename = 'posts_model';

서버 재시작 후 위와 쿼리를 입력하면 인덱싱이 되었다는 결과를 볼 수 있다.

4. 인덱싱 결과

우선 Postman에서 요청 시 기존 170 ms 에서 92 ms 로 개선된 것을 볼 수 있다.

다시 authorId가 2인 Post들을 검색하면

Index Scan using idx_post_author_id on posts_model 이라는 메세지를 확인할 수 있는데, 인덱싱이 잘 적용된 것을 볼 수 있다.

Execution Time 도 기존 121.665 ms -> 0.045 ms 로 대폭 줄어든 것을 볼 수 있다.

5. 번외 (인덱스 삭제)

DROP INDEX IF EXISTS [인덱스키값];

기존 인덱스를 지우고 싶은 경우 위와 같은 쿼리로 지울 수 있다.

다만 서버를 재시작하면 인덱싱이 진행될테니 데코레이터 된 부분들을 적절히 수정해야할 것이다.

최근댓글

최근글

skin by © 2024 ttuttak