Neo4j_事务&深度遍历

  1. 事务
    创建事务:

    这里写图片描述
    想显式地回滚一个事务,例如从一个条件代码块,可以调用failure方法,则事务将在程序块的结束做无条件的回滚。既不调用success方法也不调用failure方法也将会导致事务回滚(默认)。
    在一个事务中定义USER标签的name作为模式可索引的属性,然后使用分离的事务实际设置一个真实用户的值:

    这里写图片描述
    许多数据库管理系统使用锁机制来管理对同一个数据库的同时访问。Neo4j 事务是由清晰的读锁和写锁来控制每一个图形数据库资源(节点和关系)。

    多个读锁之间并不相互排斥,在同一时间的多线程中,可以在同一数据资源中获得多个读锁。在给定的时刻,对同一数据资源只能获得一个写锁,还必须没有其他激活的锁,不管是读锁还是写锁。

    Neo4j事务的默认设置并不自动获取读锁。因此,读的数据是节点、关系、索引实体最新提交的状态,除非在它自己的事务中,局域的修改(即使没提交)是可见的。

    写锁则是在对任意图形资源试图做变更操作时自动获得,在事务的持续时 间内都有效。每一个事务确保在完成时自动释放所有的锁。

    如果需要一个更高级别的隔离以确保其他人不能修改正在读的图形资源, Neo4j提供了一个潜在的显式获取对图形资源的读锁。程序7-5给出了相应 代码,但是这一次在读取任何的属性之前有一个额外的显式请求获取对节点的读锁。

    这里写图片描述
    当读数据时,有两个很好的理由避免使用系统的锁。就像以前提及的,首先这将产生事务的状态,并且很占内存。第二,这里主要的权衡是同时写数据,因为线程希望更新任何有显式读锁的节点或关系,在锁被释放或事务完成前是锁定的。

    如果事务A试图按节点1和节点2的顺序锁定它们,而事务B试图对相同的节点按相反的顺序获取锁,那么每一个事务可能会锁定一个节点并且无限期地等待另一个节点释放,从而产生一个无效锁。

    当无效锁发生的时候,Neo4j具有一个内置的机制对其进行探测,若有,则 抛出一个DeadlockDetectedException异常并且事务回滚。当锁定是由Neo4j专门管理时,才能进行无效锁的探测,这意味着通过依赖于Neo4j的自动默认锁定机制,或者仅仅通过使用我们前面讨论过的Neo4j API的相关锁定方法(acquireReadLock和acquireWriteLock)。如果使用任何其他的锁定机制,例如使用Java的同步(synchronized)关键词,即使发生 无效锁也不会被探测出,应用程序很有可能出现终止。

    要有效处理无效锁:第一个坚持使用Neo4j提供的核心API而不使用任何外部的锁定机制。这将确保Neo4j的无效锁探测机制是有效的,确保你的应用程序不会完全锁死,并且数据会保持一致性。在Neo4j核心API中的所有操作(除非另有指定)都是线程安全的,因此,一般不需要外部的同步。另一个是通过确保图形资源能够以同样的顺序进行访问来避免无效锁。

  2. 深度遍历

    ① 遍历的顺序
    每一次遍历器访问一个节点,它都需要决定下一步沿着哪个关系访问哪个节点,重复这个过程直至遍历结束。通过在图形中选择一个合适的路径,遍历可以很快结束并且使用很少内存。这是图论中的两个主要顺序算法,并且这两个算法用于大多数的Neo4j遍历中:深度优先(depth-first)算法和广度优先(breadth-first)算法。

    深度优先:
    深度优先顺序表明你应该首先跳到现在所在且以前没有访问过的节点的第一级子 节点。如果所有的下一代子节点都已经访问过,就应该返回具有没有访问过的子节点的第一个节点上。

    这里写图片描述
    使用Neo4j遍历API深度优先遍历整个图形:

    这里写图片描述
    深度优先是Neo4j默认的分支顺序策略。当建立遍历描述时,如果不指定遍历的顺序,则默认为深度优先。

    广度优先:
    作为广度优先算法的一部分,遍历在访问子节点之前将首先访问当前节点的所有同级节点。同级在这里是指从根节点开始与你正在访问的具有相同距离的节点。

    这里写图片描述
    1→2→3→4→5→6→7→8→9
    在广度优先遍历中,离根节点越近的节点访问得越早,将远离根节点的节点放在后面 访问。
    使用Neo4j遍历API的广度优先遍历:

    这里写图片描述

    深度优先与广度优先的比较:
    图形越大,遍历顺序对遍历性能的影响就越大。当结果节点靠近起始节点时,广度优先排序一般会给出较好的性能。但是远离起始节点时,广度优先排序总是比较慢,而深度优先排序可能非常高效,取决于结果节点是在图形的左边还是右边。

    在最坏的情况下,是整个图形都需要遍历(当结果节点在图形的右下角时),深度优先和广度优先都需要访问所有的节点。由于广度优先遍历需要更大的内存记忆遍历的足迹,这种情况下最好使用深度优先。
    除速度之外,当决定遍历的顺序时,另一个需要考虑的方面是遍历的内存消耗。遍历 时必须记住一些状态,从哪个节点来,哪个节点已经访问过。图形越大,存储这个状态需要的内存就越多。

    当使用深度优先时,你试图用最快的速度深入到图形。一旦你到了图形的最底层(访 问了一个以前没有访问过的且没有子节点的节点),你就可以完全忘掉图形的这个分支——从遍历的角度,可以认为已完成。
    广度优先遍历在开始下一深度的节点之前,试图在图形中走得尽可能广。因此,在遍历期间,需要记住所有访问过的节点和哪一个子节点还没有访问。图形越大越复杂,需要记住的节点就越多,会导致一个巨大的内存占用。
    一般来讲,每一个节点的关系越多,广度优先需要的内存就越多。
    如果结果靠近起始节点,广度优先顺序可能会更好。但是,如果图形非常密(即每一个节点有许多关系),则广度优先顺序在实际中可能会使用太多的内存。

    ② 路径扩展器
    扩展器是Neo4j遍历API的一个部件,负责决定遍历期间访问过的任意节点应该跟踪哪个关系。除选择关系类型和访向外,扩展器还负责跟踪关系的顺序。

    ③ 管理唯一性
    NODE_GLOBAL唯一性
    Neo4j遍历中最典型的唯一性设置是NODE_GLOBAL。它基本上意味着每一个节点只能访问一次并且在遍历期间只能访问一次。NODE_GLOBAL是默认的遍历设置,因此,如果你不指定唯一性,这样的设置会在你的遍历中使用。
    NODE_PATH唯一性
    NODE_PATH唯一性设置指定了从起始节点到当前节点,没有路径可以被遍历两次。多次访问同一个节点是允许的,但只有它们属于不同的路径才可以。
    RELATIONSHIP_GLOBAL唯一性
    声明图形中的每一个关系只能被访问一次。如果图形中节点之间具有多个关系,每一个节点的访问次数是连接该节点的关系数。
    RELATIONSHIP_PATH唯一性
    与NODE_PATH唯一性相似,其中的关系能被多次访问,只要从起始节点到当前节点之间的关系组合是唯一的。这种唯一性设置也可以使节点被多次访问。
    当设置NODE_GLOBAL唯一性或者RELATIONSHIP_GLOBAL唯一性时,遍历可能耗尽内存,尤其是在有很多连接的大型图形数据库中。由于唯一性的限制原因,每一个节点(在NODE_GLOBAL唯一性的情况下)或关系(在RELATIONSHIP_GLOBAL唯一性的情况下)都必须记忆。这里NODE_RECENT和RELATIONSHIP_RECENT设置开始起作用。当使用这些设置时,唯一性的限制稍微可以放宽。
    对于NODE_RECENT,记忆访问过的节点有一个上限数。这个规则对 RELATIONSHIP_GLOBAL也是一样的,即一个节点只能访问一次,但是只是最近访问的节点集合才被记忆用于比较。最近访问需要记忆的节点能够以第二个参数传递给唯一性方法
    ·NODE_LEVEL 确保处于同一级的节点(从起始节点开始具有相同距离的节点)在遍历期间只被访问一次。
    ·RELATIONSHIP_LEVEL 确保处于同一级的关系(从起始节点开始具有相同距离 的关系)在遍历期间只被访问一次。

    ④ 双向遍历
    在一个大型图形上做一个标准单向遍历可能会有低于最佳的性能。在这种情况下通过使用双向遍历,有效地利用了两个遍历去遍历图形,把问题的尺度降低了一半。对于经典的单向遍历,遍历的关系数量随着遍历的深度成指数增长。当使用双向遍历时,每一侧的遍历将只需要访问一半的图形深度,做较少的节点— 关系—节点跳跃,从而获得更好的性能。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值