协调并行事务
使用 .executeQuery()
的书签
当 使用 .executeQuery()
查询数据库 时,驱动程序会为您管理书签。在这种情况下,您可以保证后续查询可以读取以前的更改,而无需采取进一步的措施。
await driver.executeQuery('<QUERY 1>')
// subsequent executeQuery calls will be causally chained
await driver.executeQuery('<QUERY 2>') // can read result of <QUERY 1>
await driver.executeQuery('<QUERY 3>') // can read result of <QUERY 2>
要禁用书签管理和因果一致性,请在 .executeQuery()
调用中将 bookmarkManager
选项设置为 null
。
await driver.executeQuery(
'<QUERY>',
{},
{
bookmarkManager: null
}
)
单个会话中的书签
书签管理会在单个会话中运行的查询中自动发生,因此您可以相信单个会话中的查询是因果链的。
let session = driver.session({database: 'neo4j'})
try {
await session.executeWrite(async tx => {
await tx.run("<QUERY 1>")
})
await session.executeWrite(async tx => {
await tx.run("<QUERY 2>") // can read result of QUERY 1
})
await session.executeWrite(async tx => {
await tx.run("<QUERY 3>") // can read result of QUERY 1, 2
})
} finally {
await session.close()
}
多个会话中的书签
如果您的应用程序使用多个会话,您可能需要确保一个会话完成所有事务后,另一个会话才能运行其查询。
在下面的示例中,sessionA
和 sessionB
可以同时运行,而 sessionC
会等到它们的結果传播。这保证了 sessionC
要操作的 Person
节点确实存在。
const neo4j = require('neo4j-driver');
(async () => {
const URI = '<URI to Neo4j database>'
const USER = '<Username>'
const PASSWORD = '<Password>'
let driver
try {
driver = neo4j.driver(URI, neo4j.auth.basic(USER, PASSWORD))
await driver.verifyConnectivity()
} catch(err) {
console.log(`-- Connection error --\n${err}\n-- Cause --\n${err.cause}`)
return
}
await createFriends(driver)
})()
async function createFriends(driver) {
let savedBookmarks = [] // To collect the sessions' bookmarks
// Create the first person and employment relationship.
const sessionA = driver.session({database: 'neo4j'})
try {
await createPerson(sessionA, 'Alice')
await employPerson(sessionA, 'Alice', 'Wayne Enterprises')
savedBookmarks.concat(sessionA.lastBookmarks()) (1)
} finally {
sessionA.close()
}
// Create the second person and employment relationship.
const sessionB = driver.session({database: 'neo4j'})
try {
await createPerson(sessionB, 'Bob')
await employPerson(sessionB, 'Bob', 'LexCorp')
savedBookmarks.concat(sessionB.lastBookmarks()) (1)
} finally {
sessionB.close()
}
// Create (and show) a friendship between the two people created above.
const sessionC = driver.session({
database: 'neo4j',
bookmarks: savedBookmarks (2)
})
try {
await createFriendship(sessionC, 'Alice', 'Bob')
await printFriendships(sessionC)
} finally {
sessionC.close()
}
}
// Create a person node.
async function createPerson(session, name) {
await session.executeWrite(async tx => {
await tx.run('CREATE (:Person {name: $name})', { name: name })
})
}
// Create an employment relationship to a pre-existing company node.
// This relies on the person first having been created.
async function employPerson(session, personName, companyName) {
await session.executeWrite(async tx => {
await tx.run(`
MATCH (person:Person {name: $personName})
MATCH (company:Company {name: $companyName})
CREATE (person)-[:WORKS_FOR]->(company)`,
{ personName: personName, companyName: companyName }
)
})
}
// Create a friendship between two people.
async function createFriendship(session, nameA, nameB) {
await session.executeWrite(async tx => {
await tx.run(`
MATCH (a:Person {name: $nameA})
MATCH (b:Person {name: $nameB})
MERGE (a)-[:KNOWS]->(b)
`, { nameA: nameA, nameB: nameB }
)
})
}
// Retrieve and display all friendships.
async function printFriendships(session) {
const result = await session.executeRead(async tx => {
return await tx.run('MATCH (a)-[:KNOWS]->(b) RETURN a.name, b.name')
})
for(record of result.records) {
console.log(`${record.get('a.name')} knows ${record.get('b.name')}`)
}
}
1 | 使用 Session.lastBookmarks() 方法收集和合并来自不同会话的书签。 |
2 | 使用它们使用 bookmarks 参数初始化另一个会话。 |
使用书签可能会对性能产生负面影响,因为所有查询都必须等待最新的更改传播到整个集群。对于简单的用例,尝试将查询分组到单个事务中,或分组到单个会话中。 |
混合使用 .executeQuery()
和会话
为了确保部分使用 .executeQuery()
和部分使用会话执行的事务之间的因果一致性,您可以在创建会话时使用 bookmarkManager
选项,将其设置为 driver.executeQueryBookmarkManager
。由于它是 .executeQuery()
调用的默认书签管理器,因此这将确保所有工作都在同一个书签管理器下执行,从而实现因果一致性。
await driver.executeQuery('<QUERY 1>')
session = driver.session({
bookmarkManager: driver.executeQueryBookmarkManager
})
try {
// every query inside this session will be causally chained
// (i.e., can read what was written by <QUERY 1>)
await session.executeWrite(async tx => tx.run('<QUERY 2>'))
} finally {
await session.close()
}
// subsequent executeQuery calls will be causally chained
// (i.e., can read what was written by <QUERY 2>)
await driver.executeQuery('<QUERY 3>')
词汇表
- LTS
-
*长期支持* 版本是指保证支持多年的版本。Neo4j 4.4 是 LTS,Neo4j 5 也会有 LTS 版本。
- Aura
-
Aura 是 Neo4j 的完全托管云服务。它提供免费和付费计划。
- Cypher
-
Cypher 是 Neo4j 的图查询语言,它允许您从数据库中检索数据。它类似于 SQL,但适用于图。
- APOC
-
Cypher 上的出色过程 (APOC) 是一个包含(许多)函数的库,这些函数无法轻松地在 Cypher 本身中表达。
- Bolt
-
Bolt 是 Neo4j 实例和驱动程序之间交互所使用的协议。它默认情况下监听端口 7687。
- ACID
-
原子性、一致性、隔离性、持久性 (ACID) 是保证数据库事务可靠处理的属性。符合 ACID 的 DBMS 确保数据库中的数据即使在出现故障的情况下也能保持准确和一致。
- 最终一致性
-
如果数据库提供保证,即所有集群成员 *在某个时间点* 将存储数据的最新版本,则该数据库最终一致。
- 因果一致性
-
如果每个集群成员都以相同的顺序看到读写查询,则数据库是因果一致的。这比*最终一致性* 更强。
- NULL
-
空标记不是类型,而是值不存在的占位符。有关更多信息,请参见 Cypher → 使用
null
。 - 事务
-
事务是工作单元,可以*完全提交* 或在失败时*回滚*。例如银行转账:它涉及多个步骤,但它们必须*全部*成功或被恢复,以避免从一个帐户中扣款,但没有添加到另一个帐户中。
- 背压
-
背压是阻碍数据流动的力量。它确保客户端不会被它无法处理的速度更快的数据淹没。