赞
踩
Neo4j系列导航:
neo4j安装及简单实践
cypher语法基础
cypher插入语法
cypher插入语法
cypher查询语法
cypher通用语法
cypher函数语法
neo4j索引及调优
搜索性能索引通过解决节点标签/关系类型和属性谓词的特定组合,实现更快、更有效的模式匹配。通常在查询开始时,Cypher®计划器在MATCH子句中自动使用它们来扫描图形,以寻找开始模式匹配过程的最合适位置。
通过检查查询执行计划,本页将解释使用各种搜索性能索引来提高Cypher查询性能的场景。它还将提供一些关于何时使用索引的一般启发,以及关于如何避免过度索引的建议。
在创建Neo4j数据库时,默认存在两个令牌查找索引。它们在数据库中存储所有节点标签和关系类型的副本,并且只解决节点标签和关系类型谓词。
下面的查询计算类型属性值为baseball的PointOfInterest节点的数量,它将访问节点标签查找索引:
profile //profile关键字运行查询并生成其执行计划
match (n:PointOfInterest)
where n.type = 'baseball'
return count(n) //返回值26
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 count(n)
1 1 0 0 0/0 0.075 In Pipeline 1 +EagerAggregation 1 count(n) AS count(n)
1 1 0 32 116/0 8.228 Fused in Pipeline 0 +Filter 2 n.type = $autostring_0 9 26 376 116/0 8.228 Fused in Pipeline 0 +NodeByLabelScan 3 n:PointOfInterest 188 188 189 376 116/0 8.228 Fused in Pipeline 0 Total database accesses: 565, total allocated memory: 472
解读及注意点:
注意:
- 令牌查找索引非常重要,因为它们提高了Cypher查询的性能和其他索引的填充, > color=#ff00ff>删除它们将导致严重的性能下降(在上面的示例中,如果节点标签查找索引不存在,则NodeByLabelScan操作符将被AllNodesScan替换,后者在返回结果之前必须从数据库中读取所有69165个节点。)
- 不能解决任何与属性相关的谓词。
范围索引解决了大多数类型的谓词,它们用于基于范围值有效地检索数据。它们在处理具有有序、可比较值的属性时特别有用。
下面的示例首先在PointOfInterest节点的type属性上创建一个相关索引,然后再次运行上述查询,计算具有棒球类型值的PointOfInterest节点的数量:
//如果在创建索引时没有指定索引类型,Neo4j将默认创建范围索引。
create index range_index_type for(n:PointOfInterest) on (n.type)
创建相关索引后重新运行查询:
PROFILE
MATCH (n:PointOfInterest)
WHERE n.type = 'baseball'
RETURN count(n)
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 count(n)
1 1 0 0 0/0 0.057 In Pipeline 1 +EagerAggregation 1 count(n) AS count(n)
1 1 0 32 0/1 0.945 Fused in Pipeline +NodeIndexSeek 2 RANGE INDEX n:PointOfInterest(type) WHERE type = $autostring_0 5 26 27 376 0/1 > 0.945 Fused in Pipeline 0 Total database accesses: 27, total allocated memory: 472
解读及注意点:
将此查询计划与创建相关范围索引之前生成的计划(2.2)进行比较,可以发现以下变化:
几乎比不使用范围索引的查询快8倍。
这些都说明了搜索性能索引可以显著提高Cypher查询性能的基本观点。
范围索引按升序存储属性(STRING值按字母顺序存储,FLOAT和INTEGER值按数字顺序存储)。
这可能会对查询性能产生重要影响,因为规划器可能能够利用预先存在的索引顺序,因此不必在查询后面执行昂贵的Sort操作。
实例:
为了演示这种行为,下面的查询将过滤出距离属性小于30的所有ROUTE关系,并使用order BY子句按升序返回匹配关系的距离属性。
查询返回没有相关索引的结果顺序:
PROFILE
MATCH ()-[r:ROUTE]-()
WHERE r.distance < 30
RETURN r.distance AS distance
ORDER BY distance
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Ordered by Pipeline +ProduceResults 0 distance 3013 6744 0 0 0/0 14.397 distance ASC In Pipeline 1 +Sort 1 distance ASC 3013 6744 0 540472 0/0 16.844 distance ASC In Pipeline 1 +Projection 2 cache[r.distance] AS distance 3013 6744 0 84/0 22.397 Fused in Pipeline 0 +Filter 3 cache[r.distance] < $autoint_0 3013 6744 10041 84/0 22.397 Fused in Pipeline 0 +UndirectedRelationshipTypeScan 4 (anon_0)-[r:ROUTE]-(anon_1) 10044 10041 5023 376 84/0 22.397 Fused > in Pipeline 0 Total database accesses: 15064, total allocated memory: 540808
这个计划显示了关于索引和结果排序的两个要点:
UndirectedRelationshipTypeScan
操作符访问,该操作符从关系类型索引中获取所有关系及其开始和结束节点)。要了解索引如何影响查询计划,首先需要在distance属性上创建一个范围索引:
//在关系类型属性上创建范围索引以上翻译结果来自有道神经网络翻译(YNMT)· 通用场景
CREATE INDEX range_index_relationships FOR ()-[r:ROUTE]-() ON (r.distance)
重新运行查询,它现在生成一个不同的计划:
PROFILE
MATCH ()-[r:ROUTE]-()
WHERE r.distance < 30
RETURN r.distance AS distance
ORDER BY distance
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Ordered by Pipeline +ProduceResults 0 distance 301 6744 0 0 2361/10 76.542 distance ASC Fused in Pipeline 0 +Projection 1 cache[r.distance] AS distance 301 6744 0 2361/10 76.542 distance ASC Fused in > Pipeline 0 +UndirectedRelationshipIndexSeekByRange 2 RANGE INDEX (anon_0)-[r:ROUTE(distance)]-(anon_1) WHERE distance < > $autoint_0, cache[r.distance] 301 6744 3373 248 2361/10 76.542 r.distance ASC Fused in Pipeline 0 Total database accesses: 3373, total allocated memory: 312
围绕计划中同样的两点,以下内容发生了变化:
同样,使用max()和min()函数时,范围索引的顺序可以显著改善查询。
文本索引用于对string属性进行查询过滤。
如果在给定的string属性上同时存在范围和文本索引
,则文本索引将仅由Cypher规划器用于使用Scontains或ends with操作符进行查询过滤
。在所有其他情况下,将使用范围索引。
在同一属性上创建一个文本索引和一个范围索引:
//创建范围索引
create index range_index_name for(n:PointOfInterest) on (n.name)
//创建文本索引
create text index text_index_name for(n:PointOfInterest) on (n.name)
查询过滤包含“William”的name属性的所有PointOfInterest节点:
PROFILE
MATCH (n:PointOfInterest)
WHERE n.name CONTAINS 'William'
RETURN n.name AS name, n.type AS type
结果:
name | type |
---|---|
“William Shakespeare” | “statue” |
“William Tecumseh Sherman” | “equestrian statue” |
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 name, type 1 2 0 0 4/0 53.297 Fused in Pipeline 0 +Projection 1 cache[n.name] AS name, cache[n.type] AS type 1 2 0 4/0 53.297 Fused in Pipeline 0 +CacheProperties 2 cache[n.type], cache[n.name] 1 2 6 4/0 53.297 Fused in Pipeline 0 +NodeIndexContainsScan 3 TEXT INDEX n:PointOfInterest(name) WHERE name CONTAINS $autostring_0 1 2 3 > 248 4/0 53.297 Fused in Pipeline 0 Total database accesses: 9, total allocated memory: 312
将查询更改为使用STARTS WITH操作符而不是CONTAINS,则查询将使用范围索引:
PROFILE
MATCH (n:PointOfInterest)
WHERE n.name STARTS WITH 'William'
RETURN n.name, n.type
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 n.name
,n.type
1 2 0 0 4/1 1.276 Fused in Pipeline 0 +Projection 1 cache[n.name] AS n.name
, n.type ASn.type
1 2 4 4/1 1.276 Fused in Pipeline 0 +NodeIndexSeekByRange 2 RANGE INDEX n:PointOfInterest(name) WHERE name STARTS WITH $autostring_0, cache[n.name] > 1 2 3 248 4/1 1.276 Fused in Pipeline 0 Total database accesses: 7, total allocated memory: 312
解读及注意点:
点索引解决在空间点值上操作的谓词。点索引针对属性值之间的距离
或边界框内
的属性值的查询过滤进行了优化。
下面的例子创建了一个点索引,然后在一个查询中使用它,返回一个设置的边界框内所有PointOfInterest节点的名称和类型:
CREATE POINT INDEX point_index_location FOR (n:PointOfInterest) ON (n.location)
使用point.withinBBox()函数查询:
PROFILE
MATCH (n:PointOfInterest)
WHERE point.withinBBox(
n.location,
point({srid: 4326, x: -73.9723702, y: 40.7697989}),
point({srid: 4326, x: -73.9725659, y: 40.770193}))
RETURN n.name AS name, n.type AS type
结果:
name | type |
---|---|
“Heckscher Ballfield 3” | “baseball” |
“Heckscher Ballfield 4” | “baseball” |
“Heckscher Ballfield 1” | “baseball” |
“Robert Burns” | “statue” |
“Christopher Columbus” | “statue” |
“Walter Scott” | “statue” |
“William Shakespeare” | “statue” |
“Balto” | “statue” |
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) > Pipeline +ProduceResults 0 n.name
,n.type
4 8 0 0 302/0 2.619 Fused in Pipeline 0 +Projection 1 cache[n.name] AS n.name
, cache[n.type] ASn.type
4 8 0 302/0 2.619 Fused in Pipeline 0 +CacheProperties 2 cache[n.type], cache[n.name] 4 8 24 302/0 2.619 Fused in Pipeline 0 +NodeIndexSeekByRange 3 POINT INDEX n:PointOfInterest(location) WHERE point.withinBBox(location, point($autoint_0, > $autodoub 4 8 10 248 302/0 2.619 Fused in Pipeline 0 Total database accesses: 34, total allocated memory: 312
解读及注意点:
spatial.cartesian.min
和spatial.cartesian.max
:用于二维直角坐标系。spatial.cartesian-3d.min
和spatial.cartesian-3d.max
: 用于直角三维坐标系。spatial.wgs-84.min
和spatial.wgs-84.max
: 用于WGS-84二维坐标系。spatial.wgs-84-3d.min
和spatial.wgs-84-3d.max
: 用于WGS-84三维坐标系。每个设置的min和max定义了每个坐标系中空间数据的最小和最大边界。
例如,下面的索引将只存储中央公园北半部的osmnode:
CREATE POINT INDEX central_park_north
FOR (o:OSMNode) ON (o.location)
OPTIONS {
indexConfig: {
`spatial.wgs-84.min`:[40.7714, -73.9743],
`spatial.wgs-84.max`:[40.7855, -73.9583]
}
}
限制点索引的地理区域可以提高空间查询的性能。在处理复杂的大型地理空间数据以及空间查询是应用程序功能的重要组成部分时,这一点特别有用。
可以在单个属性或多个属性上创建范围索引(文本和点索引只能是单个属性)。后者称为复合索引,如果对数据库的查询经常过滤由复合索引索引的所有属性
,则这种索引非常有用。
下面的示例首先在PointOfInterest节点上为属性名称和类型创建一个复合索引,然后使用shortestPath函数查询图,以确定路径长度(根据图中的遍历关系)和动物园学校与其最近的网球场之间的地理距离(注意图中有32个唯一的PointOfInterest网球场节点):
创建复合索引:
CREATE INDEX composite_index FOR (n:PointOfInterest) ON (n.name, n.type)
对复合索引索引的两个属性使用过滤器进行查询:
PROFILE
MATCH (tennisPitch: PointOfInterest {name: 'pitch', type: 'tennis'})
WITH tennisPitch
MATCH path = shortestPath((tennisPitch)-[:ROUTE*]-(:PointOfInterest {name: 'Zoo School'}))
WITH path, relationships(path) AS relationships
ORDER BY length(path) ASC
LIMIT 1
UNWIND relationships AS rel
RETURN length(path) AS pathLength, sum(rel.distance) AS geographicalDistance
结果:
pathLength | geographicalDistance |
---|---|
25 | 2410.4495689536334 |
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Ordered by > Pipeline +ProduceResults 0 pathLength, geographicalDistance 1 1 0 0 0/0 0.065 pathLength ASC In Pipeline 3 +OrderedAggregation 1 length(path) AS pathLength, sum(rel.distance) AS geographicalDistance
1 1 50 > 5140 31/0 4.097 pathLength ASC In Pipeline 3 +Unwind 2 relationships AS rel 1 25 0 3112 > 0/0 0.180 length(path) ASC In Pipeline 2 +Projection 3 relationships(path) AS relationships 0 1 0 > 0/0 0.050 length(path) ASC +Top 4 length(path)
ASC LIMIT 10 1 0 57472 > 0/0 1.763 length(path) ASC In Pipeline 1 +Projection 5 length(path) AS length(path)
0 32 0 > 131215/1 188.723 Fused in Pipeline 0 +ShortestPath 6 path = (tennisPitch)-[anon_0:ROUTE*]-(anon_1) 0 32 181451 > 70080 131215/1 188.723 Fused in Pipeline 0 +MultiNodeIndexSeek 7 RANGE INDEX tennisPitch:PointOfInterest(name, type) WHERE name = $autostring_0 AND type = $autostring_1, RANGE > > INDEX anon_1:PointOfInterest(name) WHERE name = $autostring_2
0 31 0 376 131215/1 188.723 > Fused in Pipeline 0 Total database accesses: 181501, total allocated memory: 116040
解读及注意点:
顺序
会影响规划器使用索引求解谓词的方式。例如: 在(n.p pro1, n.p pro2, n.p pro3)上创建的复合索引将生成与在(n.p pro3, n.p pro2, n.p pro1)上创建的复合索引不同的查询计划。
下面的例子展示了在相同属性上以不同顺序定义的复合索引如何生成不同的执行计划:
//请注意在创建索引时定义属性的顺序,名称在前,名称在后,类型在后
CREATE INDEX composite_2 FOR (n:PointOfInterest) ON (n.lat, n.name, n.type)
对三个索引属性使用筛选器进行查询:
PROFILE
MATCH (n:PointOfInterest)
WHERE n.lat = 40.7697989 AND n.name STARTS WITH 'William' AND n.type IS NOT NULL
RETURN n.name AS name
结果:
name |
---|
“William Shakespeare” |
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 name 0 0 0 0 0/2 1.276 +Projection 1 cache[n.name] AS name 0 0 0 0/2 1.276 Fused in Pipeline 0 +NodeIndexSeek 2 RANGE INDEX n:PointOfInterest(lat, name, type) WHERE lat = $autodouble_0 AND name STARTS WITH $autostring_1 AND type > IS NOT NULL, cache[n.name]
0 0 1 248 0/2 1.276 Fused in Pipeline 0 Total database accesses: 1, total allocated memory: 312
该计划显示使用了最近创建的复合索引。它还显示谓词是按照查询中指定的方式过滤的(即,对lat属性进行相等性检查,对name属性进行前缀搜索,对type属性进行存在性检查)。
但是,如果在创建索引时改变了属性的顺序,则将生成不同的查询计划。为了演示此行为,首先需要删除最近创建的composite_2索引,并在以不同顺序定义的相同属性上创建一个新的复合索引。
//删除索引
DROP INDEX composite_2
//在以不同顺序定义的相同三个属性上创建复合索引
CREATE INDEX composite_3 FOR (n:PointOfInterest) ON (n.name, n.type, n.lat)
注意,属性的顺序已经改变:name属性现在是复合索引中定义的第一个属性,而lat属性是最后一个索引
创建不同的复合索引后重新运行查询
PROFILE
MATCH (n:PointOfInterest)
WHERE n.lat = 40.769798 AND n.name STARTS WITH 'William' AND n.type IS NOT NULL
RETURN n.name AS name
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 name 0 0 0 0 2/0 0.807 Fused in Pipeline 0 +Projection 1 cache[n.name] AS name 0 0 0 2/0 0.807 Fused in Pipeline 0 +Filter 2 cache[n.lat] = $autodouble_0 0 0 0 2/0 0.807 Fused in Pipeline 0 +NodeIndexSeek 3 RANGE INDEX n:PointOfInterest(name, type, lat) WHERE name STARTS WITH $autostring_1 AND type IS NOT NULL AND lat IS > NOT NULL, cache[n.name], cache[n.lat]
0 2 3 248 2/0 0.807 Fused in Pipeline 0 Total database accesses: 3, total allocated memory: 312
解读及注意点:
第一个属性
。最多可以包含一个
范围搜索或前缀搜索谓词。任意数量
。都将计划为存在性检查
。存在性检查
。注意, 如果使用了这些谓词,并且在任何已索引的(STRING)属性上也存在文本索引,那么规划器将使用文本索引
而不是复合索引。解读及注意点:
根据查询和应用程序的不同,在创建复合索引时考虑属性定义的顺序可能更经济有效
。索引主要用于查找模式的起始点
。如果查询包含一个MATCH子句,那么作为一般规则,规划器将只选择最适合该子句
中的谓词的索引。但是,如果查询包含两个或更多MATCH子句
,则可以使用多个索引
。
使用实例:
为了显示一个查询中使用的多个索引,下面的示例将首先在PointOfInterest节点的lon(经度)属性上创建一个新索引。然后,它使用一个
查询来查找中央公园威廉·莎士比亚雕像以北的所有PointOfInterest节点。
//在longitude属性上创建范围索引
CREATE INDEX range_index_lon FOR (n:PointOfInterest) ON (n.lon)
//查询查找威廉·莎士比亚雕像以北的所有PointOfInterest节点
PROFILE
MATCH (ws:PointOfInterest {name:'William Shakespeare'})
WITH ws
MATCH (poi:PointOfInterest)
WHERE poi.lon > ws.lon
RETURN poi.name AS name
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 name 9 143 0 0 233/1 1.460 Fused in > Pipeline 1 +Projection 1 poi.name AS name 9 143 283 233/1 1.460 Fused in Pipeline 1 +Apply 2 9 143 0 233/1 1.460 Fused in Pipeline 1 +NodeIndexSeekByRange 3 RANGE INDEX poi:PointOfInterest(lon) WHERE lon > ws.lon 9 143 146 2280 233/1 1.460 Fused in Pipeline 1 +NodeIndexSeek 4 RANGE INDEX ws:PointOfInterest(name) WHERE name = $autostring_0 2 1 2 376 1/0 0.635 In Pipeline 0 Total database accesses: 431, total allocated memory: 2616
该计划表明,先使用一个单独的索引来提高每个MATCH子句的性能
(首先利用name属性上的索引来查找William Shakespeare节点,然后使用lon属性上的索引来查找纵向值较大的所有节点)。
Neo4j索引不存储空值
。这意味着规划器必须能够排除空值的可能性,以便查询使用索引。
使用实例:
下面的查询通过计算所有具有未设置的name属性的PointOfInterest节点来演示空值和索引之间的不兼容性:
//查询名称值为空的节点计数
PROFILE
MATCH (n:PointOfInterest)
WHERE n.name IS NULL
RETURN count(n) AS nodes //返回值3
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline > +ProduceResults 0 nodes 1 1 0 0 0/0 0.012 In Pipeline 1 +EagerAggregation 1 count(n) AS nodes 1 1 0 32 115/0 0.769 Fused in Pipeline 0 +Filter 2 n.name IS NULL 141 3 373 115/0 0.769 Fused in Pipeline 0 +NodeByLabelScan 3 n:PointOfInterest 188 188 189 376 115/0 0.769 Fused in Pipeline 0 Total database accesses: 562, total allocated memory: 472
该计划表明,没有使用name属性上的两个可用索引(范围和文本)来解决谓词。但是,如果添加了能够排除任何空值存在的查询谓词,则可以使用索引。
使用实例:
下面的查询通过向上面的查询添加子字符串谓词来显示这一点:
//查询计数名称值为空的节点或名称属性包含'William'的节点
PROFILE
MATCH (n:PointOfInterest)
WHERE n.name IS NULL OR n.name CONTAINS 'William'
RETURN count(n) AS nodes //返回值5
查询结果现在包括在前一个查询中找到的具有未设置名称值的三个节点,以及具有包含William (William Shakespeare和William Tecumseh Sherman)名称值的两个节点。
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 nodes 1 1 0 0 0/0 0.010 In Pipeline 3 +EagerAggregation 1 count(n) AS nodes 1 1 0 32 0/0 0.220 Fused in Pipeline 2 +Distinct 2 n 141 5 0 352 0/0 0.220 Fused in Pipeline 2 +Union 3 142 5 0 352 0/0 0.220 Fused in Pipeline 2 +NodeIndexContainsScan 4 TEXT INDEX n:PointOfInterest(name) WHERE name CONTAINS $autostring_0 1 2 3 376 4/0 0.456 In Pipeline 1 +Filter 5 n.name IS NULL 141 3 373 115/0 0.673 Fused in > Pipeline 0 +NodeByLabelScan 6 n:PointOfInterest 188 188 189 376 115/0 0.673 Fused in > Pipeline 0 Total database accesses: 565, total allocated memory: 1352
解读及注意点:
确保使用索引的一种方法是通过将is not null
附加到所查询的属性,显式地过滤掉任何空值
。
使用实例:
下面的例子使用与上面相同的查询,但在WHERE子句中交换IS NULL和IS NOT NULL:
//查询名称值不为空的PointOfInterest节点计数
PROFILE
MATCH (n:PointOfInterest) WHERE n.name IS NOT NULL RETURN count(n) AS nodes //返回值185
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 nodes 1 1 0 0 0/0 0.013 In Pipeline 1 +EagerAggregation 1 count(n) AS nodes 1 1 0 32 0/1 0.691 Fused in Pipeline 0 +NodeIndexScan 2 RANGE INDEX n:PointOfInterest(name) WHERE name IS NOT NULL 185 185 186 376 0/1 0.691 Fused in Pipeline 0 Total database accesses: 186, total allocated memory: 472
该计划显示,以前在name属性上创建的范围索引现在用于解决谓词。
文本索引要求谓词只包含STRING属性
。
要在查询的任何属性可能是不兼容的类型或null而不是STRING值的情况下使用文本索引,请向查询添加类型谓词表达式IS:: STRING NOT null
(或其别名,在Neo4j 5.14中引入IS:: STRING!)这将强制属性及其STRING类型的存在,丢弃属性缺失或不是STRING类型的任何行,从而允许使用文本索引。
使用实例:
例如,如果前一个查询中的WHERE
谓词更改为附加IS:: STRING NOT NULL
,则使用文本索引而不是范围索引(范围索引不支持类型谓词表达式):
//使用类型谓词表达式进行查询
PROFILE MATCH (n:PointOfInterest) WHERE n.name IS :: STRING NOT NULL RETURN count(n) AS nodes
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 nodes 1 1 0 0 0/0 0.009 In Pipeline 1 +EagerAggregation 1 count(n) AS nodes 1 1 0 32 0/0 0.343 Fused in Pipeline 0 +NodeIndexScan 2 TEXT INDEX n:PointOfInterest(name) WHERE name IS NOT NULL 185 185 186 376 0/0 0.343 Fused in Pipeline 0 Total database accesses: 186, total allocated memory: 472
虽然在Neo4j 5.9中引入了类型谓词表达式,但是IS:: STRING NOT NULL语法在Neo4j 5.15中才成为与索引兼容的谓词。
toString函数还可用于将表达式转换为STRING值,从而帮助规划器选择文本索引。
对于仅与特定类型兼容的索引
(即文本和点索引),Cypher规划器需要推断出谓词对于不兼容的值将计算为null
,以便使用索引。如果谓词没有显式定义为所需的类型(STRING或POINT),则可能导致不使用文本或点索引的情况。
由于
类型约束保证属性总是相同的类型
,因此它们可以用于扩展文本
和点索引
与谓词兼容的场景。
为了说明这一点,下面的示例将首先删除name属性上现有的范围索引(这是必要的,因为类型约束只扩展特定于类型的索引的兼容性——范围索引不受值类型的限制)。然后,它将在创建类型约束之前和之后对name属性(存在先前创建的文本索引)使用WHERE谓词运行相同的查询,并比较结果执行计划。
使用实例:
DROP INDEX range_index_name
//查询名称值不为空的PointOfInterest节点计数
PROFILE MATCH (n:PointOfInterest) WHERE n.name IS NOT NULL RETURN count(n) AS nodes
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 nodes 1 1 0 0 0/0 0.012 In Pipeline 1 +EagerAggregation 1 count(n) AS nodes 1 1 0 32 259/0 0.363 Fused in Pipeline 0 +Filter 2 n.name IS NOT NULL 187 185 373 259/0 0.363 Fused in Pipeline 0 +NodeByLabelScan 3 n:PointOfInterest 188 188 189 376 259/0 0.363 Fused in Pipeline 0 Total database accesses: 562, total allocated memory: 472
该计划显示没有使用name属性上的可用文本索引来求解谓词。这是因为规划器无法推断出所有的名称值都是STRING类型。
但是,如果创建类型约束以确保所有名称属性都具有STRING值,则会生成不同的查询计划。
使用实例:
//在name属性上创建STRING类型约束
CREATE CONSTRAINT type_constraint FOR (n:PointOfInterest) REQUIRE n.name IS :: STRING
创建类型约束后重新运行查询:
PROFILE MATCH (n:PointOfInterest) WHERE n.name IS NOT NULL RETURN count(n) AS nodes
执行计划:
Operator Id Details Estimated Rows Rows DB Hits Memory (Bytes) Page Cache Hits/Misses Time (ms) Pipeline +ProduceResults 0 nodes 1 1 0 0 0/0 0.013 In Pipeline 1 +EagerAggregation 1 count(n) AS nodes 1 1 0 32 0/0 0.328 Fused in Pipeline 0 +NodeIndexScan 2 TEXT INDEX n:PointOfInterest(name) WHERE name IS NOT NULL 187 185 186 376 0/0 0.328 Fused in Pipeline 0 Total database accesses: 186, total allocated memory: 472
请注意,属性存在性约束目前不以相同的方式利用索引使用。
虽然不可能给出确切的指示,说明搜索性能索引何时可能对特定用例有益,但以下几点提供了一些有用的启发,说明创建索引何时可能提高查询性能
:
搜索性能索引可以显著提高查询性能。然而,出于以下原因,它们应该被明智地使用:
存储空间: 由于每个索引都是主数据库中数据的辅助副本,因此每个索引
实际上会使索引数据占用的存储空间增加一倍
。
写查询变慢: 添加索引会影响写查询的性能
。这是因为索引会随着每次写查询而更新。如果系统需要快速执行大量写操作,那么在受影响的数据实体上建立索引可能会适得其反。换句话说,如果写性能对某个特定用例至关重要,那么只在读取所需的地方添加索引可能是有益的。
由于这两点,决定索引什么(和不索引什么)是一项重要而重要的任务。
未使用的索引占用不必要的存储空间,删除它们可能是有益的。然而,要知道对数据库的查询最常使用哪些索引是很困难的。从Neo4j 5.8开始,SHOW INDEX命令返回了三个相关的列,它们可以帮助识别冗余索引
。
要返回数据库中索引的这些值(以及其他相关信息),请运行以下查询:
SHOW INDEX YIELD name, type, entityType, labelsOrTypes, properties, lastRead, readCount, trackedSince
如果识别出任何未使用的索引,使用DROP INDEX命令删除它们可能是有益的。
trackedSince列不是SHOW INDEXES命令的默认返回列的一部分。要返回此列和所有其他非默认列,请使用SHOW INDEXES YIELD *
。 ↩︎
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。