Prisma 쿼리에 관한 고찰

정재경·2024년 4월 20일
0

Prisma는 멍청하다?

유니티스의 API에서 오래 걸리는 곳을 분석해보니 Prisma가 이상한 쿼리를 만들고 있었다.
그래서 Prisma에서 코드를 어떻게 써야 그나마 덜 멍청하게 쿼리를 날리는지 알아보자.

사전 세팅

오늘 실험 대상 DB 관계는 다음과 같다. 어떤 Notice를 몇명의 학생이 읽었는지 정보를 알기 위해서 UserNotice라는 MTM 테이블이 있다. UserNotice에는 row가 약 42만개, User는 약 2500개, Notice는 약 900개 있다.

내가 원하는 데이터

  • UserNotice에서 userId (연결된 User의 deletedAt이 null인), noticeId, isRead를 전부 구해온다!

시도 1

const tmp = await this.prisma.notice.findMany({
  select: {
    userNotice: {
      where: {
        user: {
          deletedAt: null,
        },
      },
      select: {
        userId: true,
        noticeId: true,
        isRead: true,
      },
    },
  },
});

프리즈마가 생성한 쿼리

SELECT user_notice.user_id, user_notice.notice_id, user_notice.is_read
FROM "public"."user_notice" WHERE ((user_notice.user_id,user_notice.notice_id)
IN (
	SELECT "t0"."user_id", "t0"."notice_id"
    FROM "public"."user_notice" AS "t0"
    INNER JOIN "public"."user" AS "j0" ON ("j0"."id") = ("t0"."user_id")
    WHERE ("j0"."deleted_at" IS NULL AND "t0"."user_id" IS NOT NULL AND "t0"."notice_id" IS NOT NULL
)) AND user_notice.notice_id IN ($1,$2,$3, ..., $641)) OFFSET $642;

Duration: 1043ms

시도 2

const tmp2 = await this.prisma.userNotice.findMany({
  where: {
    user: {
      deletedAt: null,
    },
  },
  select: {
    userId: true,
    noticeId: true,
    isRead: true,
  },
});

프리즈마가 생성한 쿼리

SELECT user_notice.user_id, user_notice.notice_id, user_notice.is_read
FROM "public"."user_notice"
	WHERE (user_notice.user_id,user_notice.notice_id)
    IN (SELECT "t0"."user_id", "t0"."notice_id"
    	FROM "public"."user_notice" AS "t0"
	    INNER JOIN "public"."user" AS "j0" ON ("j0"."id") = ("t0"."user_id")
    	WHERE (
        	"j0"."deleted_at" IS NULL AND
            "t0"."user_id" IS NOT NULL AND
            "t0"."notice_id" IS NOT NULL
        )
    )
OFFSET $1;

Duration: 957ms

시도 3

const tmp3 = await this.prisma.user.findMany({
  where: {
    deletedAt: null,
  },
  select: {
    userNotice: {
      select: {
        userId: true,
        noticeId: true,
        isRead: true,
      },
    },
  },
});

프리즈마가 생성한 쿼리

SELECT user.id FROM "public"."user" WHERE user.deleted_at IS NULL OFFSET $1;

SELECT user_notice.user_id, user_notice.notice_id, user_notice.is_read
FROM "public"."user_notice"
WHERE user_notice.user_id IN ($1, $2, $3, ... $1433, $1434) OFFSET $1435;

Duration: 9ms + 319ms

Raw 쿼리

 SELECT user_id, notice_id, is_read
 FROM user_notice
 JOIN "user" u on user_notice.user_id = u.id
 WHERE u.deleted_at IS NULL;

Duration: 294ms

결론?

  • 셋다 결과는 똑같지만 3번 쿼리가 (그나마) 제일 낫긴 하다..
  • 조인한 테이블에 조건을 걸면 이상한 결과가 나오니 최대한 조건이 많은 테이블부터 쿼리를 시작하자...? 잘 모르겠다..
  • Prisma를 더더욱 경계하자.
profile
You'll see what I've become.

0개의 댓글

관련 채용 정보