대머리개발자

성능 - 조건절 OR 본문

개발이야기/DataBase

성능 - 조건절 OR

대머리개발자 2025. 1. 20. 15:46
728x90

조건절에 OR을 사용하면 성능에 부정적인 영향을 미칠수 있다.

그래서 어지간 하면 다른 방향으로 설계를 해야 한다.

 

성능 저하의 이뉴는

인덱스 활용 어렵고 쿼리 최적화가 어렵고 전체 테이블 스캔을 한다. 

결국 다 같은 말이다. ㅋ 당근 인덱스 활용을 안 하니  최적화 어렵고 전체 테이블 스캔을 하겠쥬.

 

가능한 경우는 IN 연산자를 활용하고 또는 UNION ALL 연산자를 사용하라고 한다.

 

내가 원하는 조건절은 아래의 내용을 만족해야 한다.

1. 기준일 상관없이 멤버쉽이면  볼 수 있는 공개 게시글 있고 

2. 멤버십으로 가입한 기준일로 볼 수 있는 조건이 있다.

// 1. 전체 공개 게시글 (날짜 조건 없음)
val publicPredicate = QBoard.board.visibility.eq(Visibility.PUBLIC)

// 2. 멤버쉽 전용 게시글 (날짜 조건 적용)
val memberPredicate = QBoard.board.visibility.eq(Visibility.MEMBER).and(QBoard.board.regDt.after(searchInfo.regDt))

// 두 조건을 OR로 연결
predicate = predicate.and(publicPredicate.or(memberPredicate))

일단 두개의 조건을 IN으로 할 수가 없다. 또한

UNION ALL 할 경우에는 페이징 처리가 지랄 맞게 된다. 물론 jpa 네이티브 쿼리 아니면 지원도 안 한다.

 

결국 "or 조건절"은 그대로 이용하는데 "조회하는 범위를 최대한 줄이는 방향으로 최적화"

 

즉, 조인을 다 하고 서브 쿼리 진행하고 정렬해서 20개를 뽑아내는 것이 아니고

먼저 20개를 뽑아내서 20개만 딱 조인하고 20개만  딱 서브 쿼리진행하는 방향으로 간다.

 

또한 

필요한 모든것을 같이(동시) 진행한다.(논-블록킹)

return withContext(Dispatchers.Default) {
    val list = async { runCatching{  searchSimple(searchInfo)  }.onFailure { mutableListOf<BoardListDto>() } }
    val count = async { try{ searchSimpleCount(searchInfo) } catch (e:Exception){0L} }
    APIResultList(list.await(),  count.await())
}



가즈아!!! 생각 길게 코드는 짧게!!

그래도 어지간하면 OR은 하지 않는 방향으로 짱구를 굴려보자. ㅠ

 

fun search(searchInfo: SearchInfo): MutableList<BoardListDto> {
    val filter = searchInfo.filter ?: Filter(pageIndex = 0, pageSize = 20, sort =  1)
    // innerJoin 절에 조건을 추가해 join 자체를 경량화 한다.
    val query:JPAQuery<BoardListDto> = jpaQueryFactory.select(searchColumns(searchInfo)).from(qBoard)
                               .innerJoin(qMenu).on(qMenu.id.eq(qBoard.menuId))

    // 본문이 내용이 경우
    if(searchInfo.includeContent == true){
        query.innerJoin(QBoardContent.boardContent).on(QBoardContent.boardContent.boardId.eq(qBoard.id))
    }

    // 예약 발행인 경우
    if(searchInfo.status == EntityStatus.B){
        query.innerJoin(qBoardBooking).on(qBoardBooking.boardId.eq(qBoard.id))
    }

    // 조회수, 좋아요수 
    query.leftJoin(qHistory).on(qHistory.targetId.eq(qBoard.id))

   // 필요한 게시판Id를 가져온다. 20개! In절이 너무 커지면 그 때 고민s ㅠ
    query.where(qBoard.id.`in`(coveringIndex(searchInfo)))
         .groupBy(qBoard.id)

    if(searchInfo.isNoticeList == true){
        query.orderBy(qBoard.noticeType.desc())
    }

    return query.orderBy(sortBy(filter.sort)).fetch()
}

 

728x90