Neo4j/Cypher_LOCK_好还是坏?
Neo4j/Cypher _LOCK_ good or bad?
假设您有 (:User)
个节点,其中有 SubscribersCount
属性.
每次来自用户 User.SubscribersCount
的某人 subscribes/unsubscribes 应该相应地更新。 [:SUBSCRIBED]
关系也将 create/deleted 用于此类操作。
在这种情况下,为了更新计数器,您可以:
- 实时计算所有传入的
[:SUBSCRIBED]
关系
- 获取
(:User)
节点和 increment/decrement 计数器的写锁
第一种方法会随着用户订阅者数量的增加而降级。
第二种方法呢?有什么缺点?
[更新(三次)]
为了开始讨论,这里有针对您的 2 个选项的 Cypher 查询示例。
根据需要计算关系计数(没有 SubscribersCount
属性):
(a) 添加关系:
MATCH (u:User {id:1234}), (v:User {id: 5678})
CREATE (u)<-[:SUBSCRIBED]-(v);
(b) 使用 SIZE
获取计数 [根据@NicoleWhite,只要使用的模式未指定标签,此 应该 在恒定时间内执行对于订阅者的节点并且 u
已经被缓存]:
MATCH (u:User {id:1234})
RETURN SIZE((u)<-[:SUBSCRIBED]-());
(c) (已弃用) 使用 COUNT
获取计数 [neo4j 必须遍历该用户的所有关系(所有类型)]:
MATCH (u:User {id:1234})
RETURN COUNT((u)<-[:SUBSCRIBED]-());
在每个 User
:
上维护一个 SubscribersCount
属性
(a) 添加一个关系 [同上,但多了一个 SET
]:
MATCH (u:User {id:1234}), (v:User {id: 5678})
CREATE (u)<-[:SUBSCRIBED]-(v)
SET u.SubscribersCount = u.SubscribersCount + 1;
(b) GET 计数[常数时间复杂度]:
MATCH (u:User {id:1234})
RETURN u.SubscribersCount;
结论
假设 u
节点被缓存后,选项 1b 确实在恒定时间内执行,那么您应该始终使用选项 1a 添加 SUBSCRIBED
关系,并使用 1b 获取这种关系的计数。维护自己的计数可能会更慢。
但是,正如@drgraduss 提醒我们的那样,如果您需要按属性过滤关系或使用标签,那么选项 1b 将不会 运行 在常数时间内。
一些例子:
SIZE(()-[:SUBSCRIBED {prop:val}]->(u))
SIZE((:label)-[:SUBSCRIBED]->(u))
在这种情况下,选项 2 可能 更好,因为 2a 和 2b 运行 在常数时间内。
假设您有 (:User)
个节点,其中有 SubscribersCount
属性.
每次来自用户 User.SubscribersCount
的某人 subscribes/unsubscribes 应该相应地更新。 [:SUBSCRIBED]
关系也将 create/deleted 用于此类操作。
在这种情况下,为了更新计数器,您可以:
- 实时计算所有传入的
[:SUBSCRIBED]
关系 - 获取
(:User)
节点和 increment/decrement 计数器的写锁
第一种方法会随着用户订阅者数量的增加而降级。
第二种方法呢?有什么缺点?
[更新(三次)]
为了开始讨论,这里有针对您的 2 个选项的 Cypher 查询示例。
根据需要计算关系计数(没有
SubscribersCount
属性):(a) 添加关系:
MATCH (u:User {id:1234}), (v:User {id: 5678}) CREATE (u)<-[:SUBSCRIBED]-(v);
(b) 使用
SIZE
获取计数 [根据@NicoleWhite,只要使用的模式未指定标签,此 应该 在恒定时间内执行对于订阅者的节点并且u
已经被缓存]:MATCH (u:User {id:1234}) RETURN SIZE((u)<-[:SUBSCRIBED]-());
(c) (已弃用) 使用
COUNT
获取计数 [neo4j 必须遍历该用户的所有关系(所有类型)]:MATCH (u:User {id:1234}) RETURN COUNT((u)<-[:SUBSCRIBED]-());
在每个
上维护一个User
:SubscribersCount
属性(a) 添加一个关系 [同上,但多了一个
SET
]:MATCH (u:User {id:1234}), (v:User {id: 5678}) CREATE (u)<-[:SUBSCRIBED]-(v) SET u.SubscribersCount = u.SubscribersCount + 1;
(b) GET 计数[常数时间复杂度]:
MATCH (u:User {id:1234}) RETURN u.SubscribersCount;
结论
假设 u
节点被缓存后,选项 1b 确实在恒定时间内执行,那么您应该始终使用选项 1a 添加 SUBSCRIBED
关系,并使用 1b 获取这种关系的计数。维护自己的计数可能会更慢。
但是,正如@drgraduss 提醒我们的那样,如果您需要按属性过滤关系或使用标签,那么选项 1b 将不会 运行 在常数时间内。
一些例子:
SIZE(()-[:SUBSCRIBED {prop:val}]->(u)) SIZE((:label)-[:SUBSCRIBED]->(u))
在这种情况下,选项 2 可能 更好,因为 2a 和 2b 运行 在常数时间内。