【问题标题】:Neo4j slow creation method [closed]Neo4j 慢速创建方法 [关闭]
【发布时间】:2015-11-02 21:14:05
【问题描述】:

在我的 Neo4j/Neo4j Spring Data 应用程序中,我有以下实体:

VoteGroup 包含 VOTED_ONVOTED_FOR 与实体 CriterionDecision 的关系以及 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 实体看起来像:

@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;
    }

}

还有。我使用基于 BaseEntity 的 Neo4j 钩子:

@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());
                }
            }
        };
    }

...

}

为了进行投票,我实现了以下方法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));
    }
...

}

@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);

}

现在,VoteGroupDaoImpl.createVote 方法的运行速度非常慢,并且延迟很大。这可能是什么原因?

添加的配置文件输出

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

Cypher 版本:CYPHER 2.2,规划器:COST。在 181 毫秒内总共 33 次 db 命中。

PROFILE Java 代码:

丰富的分析器信息:

HTML page with profiler information

【问题讨论】:

    标签: neo4j cypher spring-data-neo4j graph-databases


    【解决方案1】:

    一些可能有帮助的想法:

    1. 执行查询:

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

    从网络界面或控制台检查它的行为。尝试使用您在应用程序中使用的相同 ID。检查执行时间。

    1. VoteGroup 与 Votes 有许多关系吗?如果是,您可以删除吗:

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

    只在投票方保留有关关系的信息?您可以检查更改后的性能吗?

    1. 您能否使用某种分析器工具来确定性能问题的确切位置?现在可能还很难猜……

    2. 代码的行为是否符合预期?您在数据库中有任何重复项吗?也许您的 hashCode/equals 方法中存在错误,导致数据库中的更改超出实际应有的程度?

    【讨论】:

    • 感谢您的回答。我已经直接在 Neo4j db 上执行了这个查询,它运行得非常快。我的代码有问题。另外,当我不使用 getVoteGroupForDecisionOnCriterion() 方法时,一切都运行得很快。这个方法有什么问题?
    • 我已删除 @RelatedTo(type = CONTAINS, direction = Direction.OUTGOING) private Set&lt;Vote&gt; votes = new HashSet&lt;&gt;(); - 没有任何改变
    • 性能问题的确切位置是以下行 - VoteGroup voteGroup = getVoteGroupForDecisionOnCriterion(decision.getId(), criterion.getId());
    • 代码的行为是否符合预期? - 是的。我还在我的问题正文中添加了我的 hashCode/equals 到 VoteGroup
    • 老实说,看起来真的很奇怪......而且似乎不是您的代码有问题,而是SDN为您生成的“getVoteGroupForDecisionOnCriterion”代码。它基于本身没有性能问题的查询,您没有在代码中飞来飞去的 @Fetch 注释,所以我不知道。也许粘贴你的超类(BaseEntity)的代码只是为了检查那里是否有问题,或者只是等待一些Neo4j大师来帮助你,我感到无助......
    【解决方案2】:

    您可以尝试如下重新制定 getVoteGroupForDecisionOnCriterion 查询,以避免笛卡尔积:

    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
    

    【讨论】:

    • 感谢您的回答..性能几乎相同(原因可能是数据导入过程中VoteGroup节点的密集写入/读取操作?
    • no.. 方法 getVoteGroupForDecisionOnCriterion 即使在导入数据时也运行缓慢..
    • 我还添加了带有完整分析器信息的 HTML 页面
    【解决方案3】:

    我已迁移到新的 Neo4j 2.2.4 和 SDN 3.4.0.RC1,问题消失了

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多