Neo4j入门点滴(四):Cypher查询优化


 首先,还是老样子,清楚当前数据库中所有的内容,干干净净开始学习新的一章。
  1. match (n)-[r]-(n1)
  2. delete r,n,n1
  3. match (n)
  4. delete n
  接下来,还要使用第二篇博文中的人物和联系:
  1. CREATE (bradley:MALE:TEACHER {name:'Bradley', surname:'Green',age:24, country:'US'})
  2. CREATE (matthew:MALE:STUDENT {name:'Matthew', surname:'Cooper',age:36, country:'US'})
  3. CREATE (lisa:FEMALE {name:'Lisa', surname:'Adams', age:15,country:'Canada'})
  4. CREATE (john:MALE {name:'John', surname:'Goodman', age:24,country:'Mexico'})
  5. CREATE (annie:FEMALE {name:'Annie', surname:'Behr', age:25,country:'Canada'})
  6. CREATE (ripley:MALE {name:'Ripley', surname:'Aniston',country:'US'})
  7. MATCH (bradley:MALE{name:"Bradley"}),(matthew:MALE{name:"Matthew"})WITH bradley, matthew CREATE (bradley)-[:FRIEND]->(matthew) , (bradley)-[:TEACHES]->(matthew);
  8. MATCH (bradley:MALE{name:"Bradley"}),(matthew:MALE{name:"Matthew"})WITH bradley,matthew CREATE (matthew)-[:FRIEND]->(bradley);
  9. MATCH (bradley:MALE{name:"Bradley"}),(lisa:FEMALE{name:"Lisa"})WITH bradley,lisa CREATE (bradley)-[:FRIEND]->(lisa);
  10. MATCH (lisa:FEMALE{name:"Lisa"}),(john:MALE{name:"John"})WITH lisa,john CREATE (lisa)-[:FRIEND]->(john);
  11. MATCH (annie:FEMALE{name:"Annie"}),(ripley:MALE{name:"Ripley"})WITH annie,ripley CREATE (annie)-[:FRIEND]->(ripley);
  12. MATCH (ripley:MALE{name:"Ripley"}),(lisa:FEMALE{name:"Lisa"})WITH ripley,lisa CREATE (ripley)-[:FRIEND]->(lisa);


一、索引
  Neo4j2.0版本在标签的基础上引入了索引,可以对标签进行限制和索引。这种方式即有助于数据完整性检查,也有利于优化Cypher。上一篇博文最后介绍了限制,本篇关注度的则是索引的用法和功能 
  Neo4j的索引和其他RDBMS的定义相类似,主要用于提升节点找寻的性能。对于任何已有数据结构的更改操作索引自动更新。如果出了错而导致索引处于无效状态,便需要差错并重新生成它们。
  Cypher查询会自动使用索引,Cypher有一个查询计划器和查询优化器,可以对查询进行评估并尝试尽全力依据选索引择最短执行时间。
  创建索引的过程并不复杂:
  首先,使用如下语句对标签MALE和属性name创建一个索引:
  1. create index on :MALE(name)
  核实是否已经创建,书上说需要在neo4j-shell中使用下列命令:schema ls。但我在Web界面上使用则是报错,没有此命令。
  删除索引使用如下命令:
  1. drop index on :MALE(name)
  一旦索引建立,随后但凡在where从句中出现具有索引的属性时,不论是简单的等值比较还是其他条件,索引的使用都是自动的。然而,还有一种外显的指定索引的使用方式,就是using从句。如:
  1. create index on :MALE(name)
  2. match (n:MALE)
  3. using index n:MALE(name)
  4. where n.name = "Matthew"
  5. return n
  结果如下图所示,很遗憾在非终端中看不到查询的耗时,尽管目前的学习和测试是在Win 10下完成的,但也可以使用windows准备的Cypher-shell,可以参考另一篇文章《Neo4j入门点滴(五):Windows Shell for Cypher》。
Neo4j入门点滴(四):Cypher查询优化
  重点来了,必须记住:我们也可以在一个单一查询中使用using从句并提供多个索引项来给Cypher Query Optimizer提供索引提示,也可以使用scan给Cypher Query Planner先扫描所有标签然后再执行后续的过滤,这种做法的结果意味着优秀的性能,毕竟使用标签本身可以不必考虑那些不必要的数据。如:
  1. match (n:MALE)
  2. using scan n:MALE
  3. where n.name = "Matthew"
  4. return n
  尽管上述查询的结果是一样的,但性能会更好。需要注意的一点,使用scan时使用的是:MALE,而非index时的:MALE(name),这点一定要警惕。


、Index Sampling
  真不太好翻译,所以还是直接使用英文词组吧。
  其实,所有Cypher查询执行的第一步就是先要制订出一个有效的执行计划。尽管这个计划由Neo4j自行创建,但创建之前系统需要知道当前数据库、索引、索引所含的节点数、联系等多种重要信息。这些信息将帮助Neo4j设计一个效率高、效果好的执行计划,从而使得我们的查询请求被更快地响应。下一小节在详细讨论执行计划的过程,但是有效执行计划其中一个步骤就是Index Sampling。实际上,Index Sampling就是我们经常对索引进行分析和取样,确保索引统计数据更新,以及在数据库增删改数据的并对相应索引进行更改的全部过程。
  我们可以通过开启Neo4j数据库的下列属性实现自动index sampling(Linux下是文件neo4j.properties,windows的还没找到):
  • index_background_sampling_enabled:该布尔属性值默认被设置为False,将其改为True开启自动sampling。
  • index_sampling_update_percentage:定义了在触发sampling之前需要被改变索引的百分比阈值。
  当然,也可以在终端中输入如下命令手动开启:
  • schema sample -a:触发对所有索引做sampling。
  • schema sample -l MALE -p name:仅仅触发标签(l)和属性(p)定义的索引的sampling。

三、理解执行计划
  对于所有查询的执行计划的生成,Neo4j使用的都是基于成本的优化器(Cost Based Optimizer,CBO),用于制订精确的执行过程。可以采用如下两种不同的方式了解其内部的工作机制:
  • EXPLAIN:是解释机制,加入该关键字的Cypher语句可以预览执行的过程但并不实际执行,所以也不会产生任何结果。
  • PROFILE:则是画像机制,查询中使用该关键字,不仅能够看到执行计划的详细内容,也可以看到查询的执行结果。
  举例如下:
  1. profile match (n)
  2. where n.name = 'Annie'
  3. return n
  如下图所示:
Neo4j入门点滴(四):Cypher查询优化
  点击展开后,是这样:
Neo4j入门点滴(四):Cypher查询优化
  此外,我还发现,Shell模式下,explain和profile都不如Web界面输入的信息多,不知道是不是版本的问题,还是参数设置的问题:
Neo4j入门点滴(四):Cypher查询优化


、分析并优化查询
  还是用刚才Annie那个例子,正如所见,原查询并不高效,因为AllNodesScan需要对所有节点逐一进行匹配。然而,别忘了,Cypher引入的标签系统可不是摆着看的,试着对刚才的例子添加标签:
  1. profile match (n:FEMALE)
  2. where n.name = 'Annie'
  3. return n
  结果如下图:
Neo4j入门点滴(四):Cypher查询优化
  可以看到,优化器由AllNodesScan变为了NodeByLabelScan,过滤的过程也有原来的6-1-1缩减为当前的2-1-1。但这并没有结束,还可以进一步优化,对,就是使用index。
  1. create index on :FEMALE(name)
  其他都不变,只是提前创建一条索引,这下子优化器就可以用NodeIndexSeek,整个过程由刚才的2-1-1变成了现在的1-1-1,也就是没有任何不必要的额外付出,一击命中!
Neo4j入门点滴(四):Cypher查询优化
  具体的优化参数,可以参考estimated rows和db hits这两个数值,都是越小越好。前者指需要被扫描行数的预估值后者是系统实际运行结果的命中(I/O)绩效。其实,并不存在放之四海而皆准的通用标准(否则开发者已经就直接把这些标准内化到Neo4j中了),所以还需要积累经验,经常对Cypher的执行进行分析和优化。



五岳之巅             
2017年5月29日(孟菲斯时间)
09:10
终稿于Dorsy Ave, Memphis, TN

上一篇:DRDS到ODPS数据迁移指南


下一篇:学习的前提