programing

재귀 ORM 클래스와 관련된 스프링 저장소 성능 문제

kingscode 2023. 2. 1. 22:11
반응형

재귀 ORM 클래스와 관련된 스프링 저장소 성능 문제

목표:

리스트의 최하위 레벨(exmaple skill id 10 및 12의 경우)의 아이가 있습니다.이제 모든 자녀(이 경우 부모 34)에 대해 모든 부모(parent_id = null)를 지정하고 목록에 다시 저장합니다.결국 부모에서 각 자녀로의 경로를 원합니다(34-9-10 및 34-9-12).나중에 이들 경로(34, 9, 10, 12)의 모든 스킬을 확인합니다.

마지막으로 패치를 위에서 아래로 설명하는 기술을 모았습니다.

상황:

MariaDB(MySQL 방언)를 사용하고 있으며 다음과 같은 재귀 테이블(idSkill: 9에서 부모 34)을 가지고 있습니다.

재귀 테이블

이제 Spring Crud Repository를 사용하는 모든 부모 요소(parent_id = null)를 요청합니다.그러기 위해서는 모든 parent-element-id를 가진 목록 상에서 반복되는 루프를 사용하여 각 parent element ID에 대해 findOne(parentelementid)를 호출하고 LAGY Loading을 사용합니다.

List<Skill> parentList = skillDAO.findBySkill(null);
HashMap<Integer, ArrayList<Integer>> parentTree = customSkillDAO.findParentIdsByPersonSkills(listPersonskill);

    //Integer: Durchnummeriert zur Eindeutigkeit, von 0,1,2...
    //List: Pfad vom höchsten Vaterlement zum niedrigsten Personskill
    //Notwendig, um den Pfad pro niedrigsten Knoten auf true zu setzen
    HashMap<Integer, ArrayList<Integer>> parentTree = customSkillDAO.findParentIdsByPersonSkills(listPersonskill);
    log.info("START FINDING CHECKED");
//keySet is just numbered from 0,1,2,3...
for (int counter : parentTree.keySet()) {
        //parentTree.get(counter) gives a list whith Integer that describes the path from top to bottom.
        //So the first element is always the parent.
        mapParentSkills.put(parentTree.get(counter).get(0), new SkillDTO(skillDAO.findOne(parentTree.get(counter).get(0))));
        mapParentSkills.get(parentTree.get(counter).get(0)).setChecked(true);
    }
    log.info("START FINDING NOT CHECKED");
//Add all other parent that are not checked
for (Skill skill : parentList) {
        if (!mapParentSkills.containsKey(skill.getIdSkill())) {
            mapParentSkills.put(skill.getIdSkill(), new SkillDTO(skill));
        }
    }

    log.info("ENDE SKILLS");

나무 전체를 다 가져가고 있어.문제는 10초 정도 걸린다는 거예요.적어도 2초 안에 할 수 있도록 개선할 수 있는 방법을 알려주시겠어요?

제 수업은 다음과 같습니다.

public class Skill implements java.io.Serializable {

    public Skill() {
}


@Id
@GeneratedValue(strategy = IDENTITY)

@Column(name = "idSkill", unique = true, nullable = false)
public Integer getIdSkill() {
    return this.idSkill;
}

public void setIdSkill(Integer idSkill) {
    this.idSkill = idSkill;
}

...로드되지 않은 일부 @JsonBackReferences

@JsonBackReference
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "parent_id")
public Skill getSkill() {
    return this.skill;
}

public void setSkill(Skill skill) {
    this.skill = skill;
}

@JsonManagedReference
@OneToMany(fetch = FetchType.LAZY, mappedBy = "skill")
public Set<Skill> getSkills() {
    return this.skills;
}

public void setSkills(Set<Skill> skills) {
    this.skills = skills;
}

}

로그:

web - 2016-02-13 16:53:50,163 [http-nio-8080-exec-2] INFO c.s.컨트롤러ProfileController - 0:0:0:0:0:1 - START FINDING CHECKED 휴지 상태: levelbezei0_.idLevelBezeichnung을 idLevelB1_4_0_, levelbezeichnung을 quant_ozeichnung으로 선택합니다.휴지 상태: skills0_.parent_id를 parent_i4_15_0_, skills0_.idSkills0_15_0_, skills0_.idSkills를 levelBez3_15_1,0_1_15_0_1의 이름으로 선택합니다.

...같은 선택을 50회까지...

web - 2016-02-13 16:53:51,523 [http-nio-8080-exec-2] INFO c.s.컨트롤러Profile Controller - 0:0:0:0:0:1 - START FOUNDING 미체크 휴지 상태: skills0_.parent_id를 parent_i4_15_0_0_, skills0.idSkill을 idSkill1_15_0,0.1로 선택합니다.e skills0_.parent_id=?

..같은 선택을 몇 백번씩...

web - 2016-02-13 16:53:59,289 [http-nio-8080-exec-2] INFO c.s.컨트롤러Profile Controller - 0:0:0:0:0:0:1 - ENDE 스킬

갱신된 로그

web - 2016-02-13 19:48:25,471 [http-nio-8080-exec-2] INFO c.s.컨트롤러Profile Controller - 0:0:0:0:0:0:1 - START FINDING(검색 시작) 체크 표시

휴지 상태: levelbezei0_.idLevelBezeiknung을 idLevelB1_4_0_로, quanto_portal.levelBezeiknung levelBezeiknung에서 levelBezeiknung을 2_4_0_0_0_0_0_로 선택합니다.여기서 levelBezezezeichnung?

web - 2016-02-13 19:48:25,806 [http-nio-8080-exec-2] INFO c.s.컨트롤러Profile Controller - 0:0:0:0:0:0:1 - START FINDING이 체크되지 않음

web - 2016-02-13 19:48:25,807 [http-nio-8080-exec-2] INFO c.s.컨트롤러Profile Controller - 0:0:0:0:0:0:1 - ENDE 스킬

스킬:

public SkillDTO(Skill skill) {
    idSkill = skill.getIdSkill();
    name = skill.getName();
    levelBezeichnung = skill.getLevelBezeichnung().getBezeichnung();
    checked = skill.isChecked();
    if (skill.getSkills().size() > 0) {
        Iterator<Skill> iteratorSkill = skill.getSkills().iterator();
        while (iteratorSkill.hasNext()) {
            Skill tempSkill = iteratorSkill.next();
            skills.add(convertSkillsToProfileDTO(tempSkill));
        }
    }
}

private SkillDTO convertSkillsToProfileDTO(Skill skill) {
    return new SkillDTO(skill);
}

제공된 코드가 응용 프로그램의 기능을 이해하는 데 충분하지 않기 때문에 잘 모르겠습니다.

그러나 루프에서 너무 많은 요청을 전송하기 때문에 가능한 프로세스에 시간이 오래 걸립니다.개별 요청은 일반적으로 단일 요청보다 시간이 더 걸립니다.단일 요청으로 대체해 보십시오.예를 들어 다음과 같습니다.

@Repository
public interface skillDAO extends CrudRepository<Skill, Integer>{

   ...

   @Query("select s from Skill s where s.skill is null")
   List<Person> findRootSkills();
}

다음과 같이 사용합니다.

List<Skill> rootSkillList = skillDAO.findRootSkills();
for(Skill skill : rootSkillList){
        SkillDTO dto = new SkillDTO(skill)
        dto.setChecked(true);
        mapParentSkills.put(skill.getIdSkill(), dto);   
}

ID별 스킬을 정확하게 취득할 필요가 있는 경우parentTree다음 작업을 수행할 수 있습니다.

@Repository
public interface skillDAO extends CrudRepository<Skill, Integer>{

   ...

   @Query("select s from Skill s where s.idSkill in (:idList)")
   List<Person> findSkillsById(@Param("idList") List<Integer> idList);
}

이제 모든 ID를 수집합니다.parentTree목록을 가져옵니다.Skill오브젝트:

List<Integer> idList = new ArrayList<Integer>();
for (int counter : parentTree.keySet()) {
    idList.add(parentTree.get(counter).get(0));
}
List<Skill> rootSkillList = skillDAO.findSkillsById(idList);

//here you can fill mapParentSkills

글쎄요, 제가 시간 지연을 제대로 감지한 건가요?DTO의 방법에 지연이 있을 수 있습니다.setChecked(true)하지만 어쨌든 이것이 유용하기를 바랍니다.

갱신:

public SkillDTO(Skill skill) {
    ...
    //LOOKS LIKE NEXT LINE IS YOU PROBLEM
    if (skill.getSkills().size() > 0) {            
        Iterator<Skill> iteratorSkill = skill.getSkills().iterator();
        while (iteratorSkill.hasNext()) {
            Skill tempSkill = iteratorSkill.next();
            skills.add(convertSkillsToProfileDTO(tempSkill));
        }
    }
}

성능 문제의 원인을 찾은 것 같습니다.네 안에Skill클래스 필드skills다음과 같이 선언됨:

@JsonManagedReference
@OneToMany(fetch = FetchType.LAZY, mappedBy = "skill")
public Set<Skill> getSkills() {
    return this.skills;
}

fetch = FetchType.LAZY즉,Set메서드를 호출할 때만 로드됩니다.getSkills()따라서 매번 메서드를 호출할 때마다getSkills()JPA는 스킬 목록을 가져오기 위해 쿼리를 생성하여 DB로 전송합니다.그리고 건설자는 모든 기술 목록에 있는 모든 기술에 대해 그것을 수행합니다.시간이 많이 걸려요.교환을 시도하다fetch = FetchType.LAZY와 함께fetch = FetchType.EAGER퍼포먼스가 크게 향상될 것으로 생각됩니다.

테이블을 재설계하지 않고 캐시에 스킬을 로드했습니다.

참조: 스프링 부트 bean 데이터베이스로부터의 데이터 프리로드

언급URL : https://stackoverflow.com/questions/35372916/spring-repository-performance-issues-with-recursive-orm-class

반응형