재귀 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 |