재귀 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
'programing' 카테고리의 다른 글
| React.js - 정의되지 않은 속성을 읽을 수 없습니다. (0) | 2023.03.12 |
|---|---|
| URL용 PHP 검증/regex (0) | 2023.02.01 |
| 열 'id'가 없는 표 속편화 (0) | 2023.02.01 |
| PHP - IN 절 배열과 함께 PDO 사용 (0) | 2023.02.01 |
| 날짜가 지정된 범위 내에 있는지 확인하려면 어떻게 해야 합니까? (0) | 2023.02.01 |
