首先,还是老样子,清楚当前数据库中所有的内容,干干净净开始学习新的一章。
- match (n)-[r]-(n1)
- delete r,n,n1
- match (n)
- delete n
- CREATE (bradley:MALE:TEACHER {name:'Bradley', surname:'Green',age:24, country:'US'})
- CREATE (matthew:MALE:STUDENT {name:'Matthew', surname:'Cooper',age:36, country:'US'})
- CREATE (lisa:FEMALE {name:'Lisa', surname:'Adams', age:15,country:'Canada'})
- CREATE (john:MALE {name:'John', surname:'Goodman', age:24,country:'Mexico'})
- CREATE (annie:FEMALE {name:'Annie', surname:'Behr', age:25,country:'Canada'})
- CREATE (ripley:MALE {name:'Ripley', surname:'Aniston',country:'US'})
- MATCH (bradley:MALE{name:"Bradley"}),(matthew:MALE{name:"Matthew"})WITH bradley, matthew CREATE (bradley)-[:FRIEND]->(matthew) , (bradley)-[:TEACHES]->(matthew);
- MATCH (bradley:MALE{name:"Bradley"}),(matthew:MALE{name:"Matthew"})WITH bradley,matthew CREATE (matthew)-[:FRIEND]->(bradley);
- MATCH (bradley:MALE{name:"Bradley"}),(lisa:FEMALE{name:"Lisa"})WITH bradley,lisa CREATE (bradley)-[:FRIEND]->(lisa);
- MATCH (lisa:FEMALE{name:"Lisa"}),(john:MALE{name:"John"})WITH lisa,john CREATE (lisa)-[:FRIEND]->(john);
- MATCH (annie:FEMALE{name:"Annie"}),(ripley:MALE{name:"Ripley"})WITH annie,ripley CREATE (annie)-[:FRIEND]->(ripley);
- 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创建一个索引:
- create index on :MALE(name)
删除索引使用如下命令:
- drop index on :MALE(name)
- create index on :MALE(name)
- match (n:MALE)
- using index n:MALE(name)
- where n.name = "Matthew"
- return n
重点来了,必须记住:我们也可以在一个单一查询中使用using从句并提供多个索引项来给Cypher Query Optimizer提供索引提示,也可以使用scan给Cypher Query Planner先扫描所有标签然后再执行后续的过滤,这种做法的结果意味着优秀的性能,毕竟使用标签本身可以不必考虑那些不必要的数据。如:
- match (n:MALE)
- using scan n:MALE
- where n.name = "Matthew"
- return n
二、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:则是画像机制,查询中使用该关键字,不仅能够看到执行计划的详细内容,也可以看到查询的执行结果。
- profile match (n)
- where n.name = 'Annie'
- return n
点击展开后,是这样:
此外,我还发现,Shell模式下,explain和profile都不如Web界面输入的信息多,不知道是不是版本的问题,还是参数设置的问题:
四、分析并优化查询
还是用刚才Annie那个例子,正如所见,原查询并不高效,因为AllNodesScan需要对所有节点逐一进行匹配。然而,别忘了,Cypher引入的标签系统可不是摆着看的,试着对刚才的例子添加标签:
- profile match (n:FEMALE)
- where n.name = 'Annie'
- return n
可以看到,优化器由AllNodesScan变为了NodeByLabelScan,过滤的过程也有原来的6-1-1缩减为当前的2-1-1。但这并没有结束,还可以进一步优化,对,就是使用index。
其他都不变,只是提前创建一条索引,这下子优化器就可以用NodeIndexSeek,整个过程由刚才的2-1-1变成了现在的1-1-1,也就是没有任何不必要的额外付出,一击命中!
具体的优化参数,可以参考estimated rows和db hits这两个数值,都是越小越好。前者指需要被扫描行数的预估值,后者是系统实际运行结果的命中(I/O)绩效。其实,并不存在放之四海而皆准的通用标准(否则开发者已经就直接把这些标准内化到Neo4j中了),所以还需要积累经验,经常对Cypher的执行进行分析和优化。- create index on :FEMALE(name)
五岳之巅
2017年5月29日(孟菲斯时间)
09:10
终稿于Dorsy Ave, Memphis, TN
2017年5月29日(孟菲斯时间)
09:10
终稿于Dorsy Ave, Memphis, TN