Neo4j slow creation method [closed]

♀尐吖头ヾ 提交于 2019-12-07 17:58:48

问题


In my Neo4j/Neo4j Spring Data application I have a following entities:

VoteGroup contains relationships VOTED_ON and VOTED_FOR to entities Criterion and Decision and list of Vote

@NodeEntity
public class VoteGroup extends BaseEntity {

    private static final String VOTED_ON = "VOTED_ON";
    private final static String VOTED_FOR = "VOTED_FOR";
    private final static String CONTAINS = "CONTAINS";

    @GraphId
    private Long id;

    @RelatedTo(type = VOTED_FOR, direction = Direction.OUTGOING)
    private Decision decision;

    @RelatedTo(type = VOTED_ON, direction = Direction.OUTGOING)
    private Criterion criterion;

    @RelatedTo(type = CONTAINS, direction = Direction.OUTGOING)
    private Set<Vote> votes = new HashSet<>();

    private double avgVotesWeight;

    private long totalVotesCount;

    @Override
    public boolean equals(Object o) {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;
        VoteGroup voteGroup = (VoteGroup) o;
        if (id == null)
            return super.equals(o);
        return id.equals(voteGroup.id);
    }

    @Override
    public int hashCode() {
        return id != null ? id.hashCode() : super.hashCode();
    }
.....

}

Vote entity looks like:

@NodeEntity
public class Vote extends BaseEntity {

    private final static String CONTAINS = "CONTAINS";
    private final static String CREATED_BY = "CREATED_BY";

    @GraphId
    private Long id;

    @RelatedTo(type = CONTAINS, direction = Direction.INCOMING)
    private VoteGroup group;

    @RelatedTo(type = CREATED_BY, direction = Direction.OUTGOING)
    private User author;

    private double weight;

....
}


public class BaseEntity {

    private Date createDate;

    private Date updateDate;

    public BaseEntity() {
    }

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Date getUpdateDate() {
        return updateDate;
    }

    public void setUpdateDate(Date updateDate) {
        this.updateDate = updateDate;
    }

}

also. I use Neo4j hook based on BaseEntity:

@Configuration
@EnableNeo4jRepositories(basePackages = "com.example")
@EnableTransactionManagement
public class Neo4jConfig extends Neo4jConfiguration implements BeanFactoryAware {
    ...

    /**
     * Hook into the application lifecycle and register listeners that perform
     * behaviour across types of entities during this life cycle
     * 
     */
    @Bean
    protected ApplicationListener<BeforeSaveEvent<BaseEntity>> beforeSaveEventApplicationListener() {
        return new ApplicationListener<BeforeSaveEvent<BaseEntity>>() {
            @Override
            public void onApplicationEvent(BeforeSaveEvent<BaseEntity> event) {
                BaseEntity entity = event.getEntity();
                if (entity.getCreateDate() == null) {
                    entity.setCreateDate(new Date());
                } else {
                    entity.setUpdateDate(new Date());
                }
            }
        };
    }

...

}

in order to make a vote, I have implemented following method VoteGroupDaoImpl.createVote:

@Service
@Transactional
public class VoteGroupDaoImpl implements VoteGroupDao {

    @Autowired
    private VoteRepository voteRepository;

    @Autowired
    private VoteGroupRepository voteGroupRepository;

    @Override
    public Vote createVote(Decision decision, Criterion criterion, User author, String description, double weight) {
        VoteGroup voteGroup = getVoteGroupForDecisionOnCriterion(decision.getId(), criterion.getId());
        if (voteGroup == null) {
            voteGroup = new VoteGroup(decision, criterion, weight, 1);
        } else {
            long newTotalVotesCount = voteGroup.getTotalVotesCount() + 1;
            double newAvgVotesWeight = (voteGroup.getAvgVotesWeight() * voteGroup.getTotalVotesCount() + weight) / newTotalVotesCount;
            voteGroup.setAvgVotesWeight(newAvgVotesWeight);
            voteGroup.setTotalVotesCount(newTotalVotesCount);
        }
        voteGroup = voteGroupRepository.save(voteGroup);

        return voteRepository.save(new Vote(voteGroup, author, weight, description));
    }
...

}

and

@Repository
public interface VoteGroupRepository extends GraphRepository<VoteGroup>, RelationshipOperationsRepository<VoteGroup> {

    @Query("MATCH (d:Decision)<-[:VOTED_FOR]-(vg:VoteGroup)-[:VOTED_ON]->(c:Criterion) WHERE id(d) = {decisionId} AND id(c) = {criterionId} RETURN vg")
    VoteGroup getVoteGroupForDecisionOnCriterion(@Param("decisionId") Long decisionId, @Param("criterionId") Long criterionId);

}

Right now, method VoteGroupDaoImpl.createVote works really slow with a huge latency .. what can be a reason of that ?

ADDED PROFILE output

for

MATCH (d:Decision)<-[:VOTED_FOR]-(vg:VoteGroup)-[:VOTED_ON]->(c:Criterion) WHERE id(d) = {decisionId} AND id(c) = {criterionId} RETURN vg

Cypher version: CYPHER 2.2, planner: COST. 33 total db hits in 181 ms.

PROFILE Java code:

Rich profiler information:

HTML page with profiler information


回答1:


Some ideas that may help:

  1. Execute the query:

    MATCH (d:Decision)<-[:VOTED_FOR]-(vg:VoteGroup)-[:VOTED_ON]->(c:Criterion) WHERE id(d) = {decisionId} AND id(c) = {criterionId} RETURN vg

from the web interface or the console and check how it behaves. Try for the same ids you use in the app. Check what is the execution time.

  1. Has VoteGroup numerous relations to Votes? If yes, can you remove:

    @RelatedTo(type = CONTAINS, direction = Direction.OUTGOING) private Set<Vote> votes = new HashSet<>();

and keep information about relation on the Vote side only? Can you check the performance after that change?

  1. Can you use some kind of a profiler tool to identify the exact place of performance problems? Right now it may be still difficult to guess...

  2. Does the code behave as it should? Don you have any duplicates in the DB? Maybe you have bugs in your hashCode/equals methods that cause much more changes in the DB than there really should be?




回答2:


You could try to reformulate the getVoteGroupForDecisionOnCriterion query as follows, in order to avoid the cartesian product:

MATCH (d:Decision) WHERE id(d) = {decisionId}
WITH d MATCH (c:Criterion) WHERE id(c) = {criterionId}
WITH d,c MATCH d<-[:VOTED_FOR]-(vg:VoteGroup)-[:VOTED_ON]->c
RETURN vg



回答3:


I'm moved to new Neo4j 2.2.4 and SDN 3.4.0.RC1 and the issue disappeared



来源:https://stackoverflow.com/questions/31928321/neo4j-slow-creation-method

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!