cron을 활용해서 정해진 시간마다 DB에서 정보를 재귀적으로 탐색하던중 쓰레드를 처리하는데 시간이 너무 오래 걸린다는 것을 발견했다.
성능 향상을 위해서 재귀 메소드를 query로 변경하기로 했고 성능을 테스트해보기로 했다.
재귀 메소드와 쿼리의 속도 측정
1. Java Jpa 재귀메소드
2. mysql recursive cte
3. 각각 작업시간을 측정한다.
Java
// 활동중인 자신의 후임들이 몇명인지 재귀적으로 찾아낸다.
public class UnitEntity {
int id; // id
int upperUnitId; // 선임
boolean active; // 현재 활동 여부
}
// 재귀 메소드
// 재귀는 나의 후임이 없으면 멈춘다.
public class RecursiveFind{
private static int unitCount = 0;
public void countMyUnderUnit(int unit_entity_id) {
// 1. parameter를 선임으로 하는 unit을 찾는다.(parameter의 후임 찾기)
List<UnitEntity> under_unit_list = unitRepository.findUnitEntitiesByUpperUnitId(unit_entity_id);
if(under_unit_list.size == 0) return;
// 반복문을 통해 원하는 조건을 확인한다.
for (UnitEntity unit : under_unit_list) {
// 2. 활동 여부를 판단해서 숫자를 센다.
if(unit.getActive){
unitCount++;
}
// 3. 반복 !
countMyUnderUnit(unit.getId());
}
}
}
mysql
with recursive cte (id, upperUnitId, active) as
(
select id,
upperUnitId,
active
from unit upper_unit
where upper_unit = <parameter 입력!>
union all
select
id,
upperUnitId,
active
from unit under_unit
inner join cte on under_unit.upeerUnitId= cte.id
)
select id, upperUnitId, active from cte where active = 1;
구동 환경에 따라서 차이가 있겠지만 메소드 활용보다 쿼리가 압도적인 성능을 보여주었다.
jpa를 활용하면 대량의 데이터를 처리하는데 불리하다는 것을 알게 되었다.
그래서 대용량 조회 및 데이터 수정,삭제는 nativeQuery를 사용하는 것이 더욱 낫겠다는 결론을 내렸다.
+@ repository에서 update와 delete를 사용할때
@Modifying
@Transactional
Anotation을 붙여야 한다.
ex)
public interface UnitRepository extends JpaRepository<UnitEntity, Integer> {
// error 발생
@Query(value = "update
unitEntity set upperUnitId = :upperUnitId
where id = :id", nativeQuery = true)
void updateQuery(@Param("id")int id ,@Param("upperUnitId")int upperUnitId);
// 수정
@Modifying
@Transactional
@Query(value = "update
unitEntity set upperUnitId = :upperUnitId
where id = :id", nativeQuery = true)
void updateQuery(@Param("id")int id ,@Param("upperUnitId")int upperUnitId);
}
조금더 테스트를 진행해보고 결과를 남겨야겠다.