将显式 Lucene 索引迁移到原生模式索引
鉴于仍有一些客户使用较旧的 Neo4j 版本并采用 legacy/explicit 索引,我们将在此讨论一些要点,说明在升级到 Neo4j 4.x 版本时如何将这些索引转换为本机模式索引,因为 legacy/explicit 索引自 3.5.x 起已被弃用,并在 4.x 版本中被完全移除。
背景是,Legacy/explicit 索引是 Neo4j 3.2(发布日期 2017)之前唯一可用的索引类型。这些早期版本的索引是基于 Lucene 实现的,通常会导致显著的写入性能下降,因此需要用 3.3 及以上版本提供的本机模式索引来替代它们。
除了性能方面的考量外,显式索引在通过 Java API(以及自 3.3.x 起的存储过程)添加/删除/更新节点、关系或属性时,还必须显式/手动保持最新,这也是它们被称为显式索引的原因。
相反,本机索引会自动维护,从而无需任何手动步骤或额外代码来保持其最新。
从实现角度来看,这些 legacy 索引几乎不包含模式细节,除了索引创建时原作者选择的命名约定外,几乎没有可供使用的线索来帮助以脚本方式实现自动迁移到 Neo4j 本机模式索引的过程。
因此,任何索引迁移都必须逐个索引进行,可能需要一定程度的猜测。这意味着需要查看 API 代码,评估哪些节点或关系属性被索引,然后相应地在这些属性上实现相匹配的模式索引,或者审查 Cypher 查询并制定支持性索引以优化其执行。
总体而言,以下是推荐的高级别步骤
1) 获取当前版本中所有索引的列表
-
示例:在 3.x 中,您可以使用 "call db.index.explicit.list();",它会显示索引名称以及其类型,类型可以是 “exact” 或 “full-text”,并指明是节点索引还是关系索引,这有助于您确定要构建何种模式索引。
2) 接下来查看您的 Cypher 或 Java API 代码,并相应地将其转换为 4.x+ 格式。
-
例如,将 “start” 语句转换为等价的 MATCH 语句。示例
-
START n=node:myExplicitIndexYear("myid:1234567") RETURN n;
-
上述查询应改为如下
-
MATCH (n:Person{myid:1234567}) RETURN n;
-
3) 实现等价的模式索引,以支持并优化步骤 2 中关联/转换后的查询的执行。
-
示例:CREATE INDEX index_name FOR (n:Person) ON (n.myid);
-
更具体地说
-
检查所有 Cypher 查询,了解它们实际写入该索引的内容,并据此生成相应的 CREATE INDEX(针对整数、字符串、日期、地理空间等)或调用 db.index.fulltext.createIndexForNodes(全文检索)语句。
-
检查所有对该索引的读取,并采用语句使用新索引(这意味着需要启用查询日志,以捕获生成的 Cypher 查询及其性能属性,如执行时间、IO、内存、CPU 等,以标记运行时间最长的查询并逐个进行排查——而且您的查询模式很可能相互类似,因此修复一个查询后,同类查询也会受益)。
-
4) 完成所有显式索引的处理后,关闭数据库并移动/删除相关的索引子文件夹(例如 /data/databases/graph.db/index)。
请注意,如果在将 legacy 索引转换为等价的模式索引后,使用 legacy 索引的搜索查询返回的行数与模式索引不符(出现意外的行数),这可能表明模式索引定义不正确(标签或属性错误),或者 legacy 索引未及时更新(记住 legacy 索引必须手动保持最新,例如,如果您的 Java API 代码缺乏适当的错误处理,则在因服务器重启等原因导致回滚时,事务可能没有被正确重试和提交)。以下是一个 3.x 示例,演示了此类情况。
// Create New Nodes
//
CREATE (p:Person {name:'Steve_1',year:1990});
CREATE (p:Person {name:'Steve_2',year:1990});
CREATE (p:Person {name:'Steve_3',year:1990});
// Create Two Explicit Indexes(also called Legacy/Manual/Lucene Indexes)
//
CALL db.index.explicit.forNodes('myExplicitIndexFTS', {type: 'fulltext', provider: 'lucene'});
CALL db.index.explicit.forNodes('myExplicitIndexYear', {type: 'exact', provider: 'lucene'});
// List Explicit Indexes
//
call db.index.explicit.list;
// Index nodes with name property
//
MATCH (p:Person{name:'Steve_1'}) call db.index.explicit.addNode('myExplicitIndexFTS', p, 'name', 'Steve_1') yield success return count(*);
MATCH (p:Person{name:'Steve_2'}) call db.index.explicit.addNode('myExplicitIndexFTS', p, 'name', 'Steve_2') yield success return count(*);
// Index nodes with "birth year" property
//
match (p:Person{name:'Steve_1'})
with p
call db.index.explicit.addNode("myExplicitIndexYear",p,"year",p.year) yield success return count(*);
// Search for persons with matching name VE, which will only return Steve_1, and Steve_2
//
CALL db.index.explicit.searchNodes('myExplicitIndexFTS','name:*VE*');
// Search for persons with year=1990 which will only return Steve_1
//
CALL db.index.explicit.searchNodes('myExplicitIndexYear','year:1990');
// Search for persons with year=1990 using explicit index which will only return Steve_1
//
start n=node:myExplicitIndexYear("year:1990") return n;
// Convert "start" to equivalent Match statement, and this statement returns all 3 rows corresponding to year=1990 (and of course ideally, you would want to create an index on :Person(
year) or :Person(name) for best performance when creating equivalent native schema indexes on these two properties.
match (n:Person{year:1990}) return n;
附录
-
欲获取每个版本的已弃用功能完整列表,请访问以下链接: /docs/cypher-manual/current/deprecations-additions-removals-compatibility/
-
如上所述,3.3.x 还提供了使用存储过程实现显式索引,以便在不使用 Java API 的情况下更易实现,但这些索引仍需手动维护。此外,这些存储过程在 3.5.x 起已被弃用,并在 4.0、4.1、4.2 中被新的存储过程取代(仅用于全文搜索用例),文档见此处: /docs/cypher-manual/current/administration/indexes-for-full-text-search/
-
另外,根据 4.3.0 更新日志,所有 FTI(全文检索)过程均已弃用。
-
"引入 CREATE FULLTEXT INDEX … Cypher 命令。弃用 db.index.fulltext.createNodeIndex、db.index.fulltext.createRelationshipIndex 和 db.index.fulltext.drop。"
-
-
需要修改/删除的 3.x 之前 Java API 代码文档
此页面有帮助吗?