《Neo4j High Performance》by Sonal Raj ——索引与优化部分

本文探讨了如何优化Neo4j中的查询性能,包括避免全局数据扫描、编制索引和约束、减少笛卡尔积生成、优化MATCH模式等高级Cypher技巧。同时,也介绍了图模型优化方法,如显式定义关系和属性重构,以及索引的效益与权衡。强调了在设计数据库时考虑查询优化的重要性,并提醒注意不要依赖Neo4j的内部ID进行查询。

Part 2 Querying and Indexing in Neo4j

翻译来自DeepL和百度翻译(理性参考)。

高级Cypher技巧

Cypher是一种高效的语言,不仅使查询更简单,而且 还致力于最大限度地优化结果生成过程。还有很多 在性能方面的优化,可以借助于与应用程序的数据领域有关的知识来实现。在用于重组查询的应用程序的数据域的帮助下,可以实现更多的性能优化。

查询优化

💡 方案:
**0.避免全局数据扫描:**优化查询性能的手动模式取决于开发者为减少遍历域和确保在结果中只获得基本数据所做的努力。全局扫描会搜索整个图,这对较小的图来说没有问题,但对大数据集来说就不行了。——>在图形和查询中使用标签可以帮助优化搜索 模式的搜索过程。

#错误示范 
START n =node(*)
MATCH (n)-[:KNOWS]-(m)
WHERE n.identity = "Batman"
RETURN m
#正确示范 
START n =node(*)
MATCH (n:superheroes)-[:KNOWS]-(m)
WHERE n.identity = "Batman"
RETURN m

💡 **1.编制索引和约束条件以加快搜索速度:**在图空间中的搜索可以被优化并使之更快 如果数据被编入索引,或者我们对其应用某种 的约束。这样一来,遍历就避免了多余的匹配,而直接进入所需的索引位置。直接进入到所需的索引位置。

#添加索引 
CREATE INDEX ON: superheroes(identity)
#添加约束 
CREATE CONSTRAINT ON n:superheroes
ASSERT n.identity IS UNIQUE

💡 **2.避免笛卡尔积生成:**在创建查询时,我们应该包括以某种方式连接的实体。使用不特定或不相关的实体最终可能会产生大量未使用或意外的结果。

#错误示范 
MATCH (m:Game), (p:Player)
#避免笛卡尔积 

MATCH ( a:Actor), (m:Movie), (s:Series)
RETURN COUNT(DISTINCT a), COUNT(DISTINCT m), COUNT(DISTINCTs)
#优化方式 
MATCH (a:Actor)
WITH COUNT(a) as actors
MATCH (m:Movie)
WITH COUNT(m) as movies, actors
MATCH (s:Series)
RETURN COUNT(s) as series, movies, actors

💡 **3.在MATCH中使用更多的模式,而不是WHERE:**建议在MATCH子句中保留大部分的模式。WHERE子句并不完全是为了模式匹配;相反,当与START和WITH一起使用时,它是用来过滤结果的。然而,当与MATCH一起使用时,它实现了对所述模式的约束。因此,当你将模式与MATCH部分一起使用时,模式匹配会更快。在找到起点之后–无论是通过使用扫描、索引还是已经绑定的点–执行引擎将使用模式匹配来找到匹配的子图。由于Cypher是声明性的,它可以改变这些操作的顺序。WHERE子句中的谓词可以在模式匹配之前、期间或之后被评估。

💡**4.进一步拆分MATCH模式:**与其在同一个MATCH语句中以逗号分隔的方式出现多个匹配模式,你可以在几个不同的MATCH语句中分割这些模式。这个过程大大减少了查询时间,因为它现在可以在每个连续的匹配阶段在较小的或减少的数据集上进行搜索。

💡**5.对查询进行剖析:**你可以在响应的配置文件中监控你的查询的处理细节,你可以通过PROFILE关键字实现,或者在提出请求时将配置文件参数设置为True。一些有用的信息可以以_db_hits的形式出现,告诉你一个实体(节点、关系或属性)被遇到了多少次。

💡**6.查询中的参数:**Cypher的执行引擎试图优化并将查询转化为相关的执行计划。为了优化用于这一任务的资源量,与字面意思相比,使用参数是首选。通过这种技术,Cypher可以重新利用现有的查询,而不是解析或编译基于字面的查询来建立新的执行计划。

图模型优化

有时,查询优化可以成为使用Neo4j提高应用程序性能的好方法,但你可以在定义数据库时加入一些基本的做法,这样可以使事情变得更简单,使用起来更快。

💡 方案:
**1.显式定义:**如果我们正在处理的图形模型包含组件之间的隐式关系。当我们以明确的方式定义这些关系时,可以实现更高的查询效率。这会导致更快的比较,但它也有一个缺点,即现在图形需要更多的存储空间,以便为所有出现的数据添加一个额外的实体。
显示定义示例

💡 **2.属性重构:**这是指在WHERE或MATCH子句中复杂的耗时操作可以直接作为属性包含在图的节点中的情况。这不仅节省了计算时间,导致查询速度大大加快,而且还导致了图数据库中更有组织的数据存储实践,以实现实用性。

索引

只有当你的查询中最常用的属性都有索引时,查询的性能才会得到优化。否则,如果Neo4j对一个属性进行了索引,而检索另一个属性的匹配需要遍历所有的节点,那么Neo4j就没有索引的效用。你可以将搜索中要用到的属性聚合成一个属性,并为其单独建立索引以提高性能。

目前,模式索引不支持在同一索引下对标签的多个属性进行索引。但是,你可以对同一标签下的节点的不同属性使用多个索引。索引会占用数据库的空间,所以当你觉得不再需要某个索引时,解除数据库对这种索引的负担是很好的。

索引效益和权衡

索引并不是免费的。由于底层的应用程序代码负责管理和使用索引,应该仔细考虑所遵循的策略。不适当的决定或索引中的缺陷会导致性能下降或不必要地使用磁盘存储空间。

在索引的权衡清单上,最重要的是索引结果会使用存储空间,也就是说,被索引的实体数量越多,磁盘使用量就越大。为数据创建索引基本上是创建小的查找图或表的过程,以允许快速访问图中的数据。因此,对于诸如INSERT或UPDATE这样的写操作,我们要写两次数据,一次是创建节点,另一次是将其写到索引映射中,索引映射中存储了一个指向创建节点的指针。

此外,随着索引数量的增加,插入和更新的操作将花费相当多的时间,因为与创建或更新实体相比,执行索引的操作几乎一样多。由于更新/插入现在需要修改该实体的索引,代码库自然会扩大,如果你对查询的时间进行剖析,插入一个有索引的节点的时间大约是插入没有索引时的两倍。

另一方面,索引的好处是查询性能得到了很大的提高,因为图中的大段内容被从搜索域中剔除了。
!!请注意,为了实现快速查询而将Neo4j生成的ID存储在外部并不是一个好的做法,因为这些ID可能会被修改。节点和关系的ID是一种内部表示方法,明确地使用它们可能会导致流程中断。

因此,对于不同的应用程序,索引场景会有所不同。与读取操作相比,那些需要更频繁更新或创建的操作应该轻度使用索引实体,而主要处理读取的应用程序应该大量使用索引来优化性能。

### 3.1 多跳检索中的结构向量索引集成 在 Neo4j 中实现多跳检索(multi-hop retrieval)的关键在于将结构遍历向量相似性搜索结合使用Neo4j 从 5.11 版本开始支持向量索引,并允许在节点或关系上创建向量嵌入索引,从而实现高效的语义搜索。通过结构进行多跳查询时,可以结合向量索引在每一跳中引入语义匹配能力,从而提升检索的准确性和智能化水平。 例如,在一个知识谱中,用户可能希望从一个实体出发,通过多个关系路径找到其语义相关的最终实体。此时可以在每一跳中使用向量索引进行语义匹配,以筛选出最相关的候选节点。创建向量索引的语句如下: ```cypher CREATE VECTOR INDEX doc_embedding_index FOR (n:Doc) ON (n.embedding) OPTIONS {indexConfig: { `vector.dimensions`: 1024, `vector.similarity_function`: 'cosine' }} ``` 该语句定义了一个向量索引,用于在节点标签 `Doc` 的 `embedding` 属性上执行基于余弦相似度的向量搜索 [^4]。 ### 3.2 多跳检索的联合查询实现 在实际查询中,可以通过 Cypher 语言将遍历向量相似性函数结合。例如,以下查询展示了如何在多跳路径中引入向量匹配: ```cypher MATCH path = (d:Disease {name: "糖尿病"})-[:TREATS]->(m:Medicine)-[:RELATED_TO]->(r:Research) WHERE vector.similarity.cosine(r.embedding, $queryVector) > 0.7 RETURN path ORDER BY vector.similarity.cosine(r.embedding, $queryVector) DESC LIMIT 5 ``` 该查询首先通过结构找到“糖尿病”治疗相关的药物节点,再查找这些药物所关联的研究节点,并使用向量相似性对研究节点进行排序,最终返回最相关的路径 [^1]。 ### 3.3 结构引导的语义增强检索 在多跳检索中,结构不仅提供路径导航功能,还能作为语义上下文的来源。例如,在医疗问答系统中,可以通过遍历获取用户查询相关的实体集合,再基于这些实体的向量表示进行语义扩展检索。这种机制使得系统能够在结构中动态调整检索路径,并结合向量数据库的语义匹配能力,提高检索结果的相关性。 Neo4j 提供了内置的向量相似性函数,如 `vector.similarity.cosine` 和 `vector.similarity.euclidean`,支持在查询中直接进行向量匹配 。 ### 3.4 代码示例:多跳检索向量排序结合 以下是一个 Python 示例,展示如何在 Neo4j 中结合结构向量索引实现多跳检索: ```python from langchain.graphs import Neo4jGraph from langchain.vectorstores import Neo4jVector from langchain.embeddings import OpenAIEmbeddings # 初始化图数据库连接 graph = Neo4jGraph( uri="neo4j://localhost:7687", username="neo4j", password="your_password" ) # 初始化向量检索器 vectorstore = Neo4jVector.from_existing_index( embedding=OpenAIEmbeddings(), index_name="research_embeddings" ) # 执行多跳查询 graph_query = """ MATCH path = (d:Disease {name: "糖尿病"})-[:TREATS]->(m:Medicine)-[:RELATED_TO]->(r:Research) RETURN r.embedding AS embedding, r.title AS title """ graph_results = graph.query(graph_query) # 提取向量并进行语义排序 query_vector = OpenAIEmbeddings().embed_query("糖尿病的最新研究进展") similarities = [] for result in graph_results: similarity = vectorstore._similarity(query_vector, result["embedding"]) similarities.append((result["title"], similarity)) # 按照语义相似度排序 similarities.sort(key=lambda x: x[1], reverse=True) for title, score in similarities[:5]: print(f"Title: {title}, Similarity: {score}") ``` 该代码通过结构检索出“糖尿病”相关的研究节点,再使用向量相似性对这些节点进行排序,从而实现多跳检索语义匹配的结合 [^1]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值