当前位置: 首页 > 工具软件 > neo4j-client > 使用案例 >

neo4j-3.x性能调优

贾成天
2023-12-01

1.配置优化

优化内存配置

在neo4j中有两个内存配置项,直接决定了neo4j的性能。在neo4j中最重要的内存的配置有两项:

  • 堆内存(Heap size)

堆内存是neo4j运行时需要的内存,用于:

  • 查询执行
  • 事务状态
  • 图形管理等

堆的大小决定于neo4j的使用性质。配置堆内存的参数如下:

dbms.memory.heap.initial_size=16384m
dbms.memory.heap.max_size=16384m

说明:官方建议堆内存不要过大,过大容易产生full gc,导致程序停顿。在总物理内存足够的情况下(>56G),官方的建议值是:16G。

  • 页缓存(pagecache)
dbms.memory.pagecache.size=80g

在neo4j中,"页面缓存”用来缓存索引和数据。通过该参数的设置,可以把数据都加载到内存中。该参数的大小需要根据数据量和索引量,以及物理内存的大小来设置。

为查询的字段创建索引

  • 创建单属性索引
CREATE INDEX ON :Person(firstname)
  • 创建复合索引
CREATE INDEX ON :Person(age, country)

注意:只有复合索引的两个字段都被查询,复合索引才被使用,否则不会被使用。

  • 查看索引
CALL db.indexes

注意:当看到state这一栏的状态为"ONLINE"时,索引才生效。

尽可能的建立更多种类的关系标签,而不是添加手工索引

尽可能的建立更多种类的关系标签, 而不是给关系加属性, 这样会很低效, 即使给关系加上手工索引。

使用neo4j-import

  • 生产环境用neo4j-import加载数据很快,但得先构造很多个csv文件,另外该命令只用于neo4j初始化状态。

  • 增量数据的同步加载又不想暂停数据库服务,需要使用:load csv 或者apoc

图数据库的定位

适合做图存储和基于模式匹配的子图查询,并不适合做大量与搜索无关的数据存储工作,比如:元数据查询就不应该放到图数据库中。

操作系统的文件系统优化

  • I/O调度
    在Linux上,驱动程序的I / O调度程序(在本例中为sda)可以在运行时更改,如下所示:
$ echo 'deadline' > /sys/block/sda/queue/scheduler
$ cat               /sys/block/sda/queue/scheduler
noop [deadline] cfq

调整垃圾收集器

使用G1垃圾回收器,neo4j-3.5的默认启动参数应该是G1。也可以通过jsp -lv查看一下。

-XX:+UseG1GC

服务线程池的设置

为客户端(client)建立的每个连接都分配给连接器的线程池。空闲连接不会消耗服务器端的任何资源,并且会针对来自客户端的消息对其进行监视。到达连接的每条消息都会触发线程池中可用线程上的连接调度。

Bolt线程池具有最小和最大容量。 它从可用的最小线程数开始,并根据工作负载增长到最大数量。 闲置超过指定时间段的线程将停止并从池中删除,以释放资源。 但是,池的大小永远不会低于最小值。

以下配置选项可用于配置Bolt连接器:

选项默认值描述
dbms.connector.bolt.thread_pool_min_size5即使空闲也将始终保持最小线程数。
dbms.connector.bolt.thread_pool_max_size400线程池将创建的最大线程数。
dbms.connector.bolt.thread_pool_keep_alive5m线程池在从池中终止空闲线程之前将等待的持续时间。但是,线程数永远不会低于dbms.connector.bolt.thread_pool_min_size。

说明:在并发不大的情况下该参数可以不用设置。

2. 首先进行数据预热

才启动时需要对neo4j的数据进行预热,通过以下命令进行预热:

MATCH (n) OPTIONAL MATCH (n)-[r]->() RETURN count(n.name) + count(r)

注意:若数据量很大,预热的时间可能会比较长。我这里100G内存,16Gheapsize+80gpagecache,32CPU,使用了56分钟才预热完成。
可以根据内存大小,调整预热的条件,可以把数据预热到内存。

3. 学会查看执行计划

可以通过explain(不会执行语句)语句来查看执行计划,比如:是否使用索引等。

explain match data=(na)-[r]->(nb:company{name:'ss'}) return data

在这里查看数据查询执行计划输出的解释

注意:还有一个命令可以查看执行计划,但profile命令会执行语句,explain是不会执行查询语句。

指标说明

关注指标:

  • estimated rows: 需要被扫描行数的预估值
  • dbhits: 实际运行结果的命中绩效

说明:两个值都是越小越好。

4. 优化查询语句

尽量在match子句中设置过滤条件

  1. 例子1
  • 原语句:
MATCH (a:Author)-[:author_is_in_field]->(f:Field)
WHERE f.field_level = "L3"
RETURN a.auhtor_name,f.field_name,f.field_reference_count
LIMIT 10
  • 优化后的语句
MATCH (a:Author)-[:author_is_in_field]->(f:Field{field_level:"L3"})
RETURN a.auhtor_name,f.field_name,f.field_reference_count
LIMIT 10
  1. 例子2
MATCH (b:BANK {bank_id : 'bank536039'}),(c:COMP {comp_id : 'comp323275' }) return count(b),count(c);

这种写法查询性能会很快,毫秒级返回。

查询时一定要设置类型(也叫标签)

没有指定类型(标签)的查询是非常耗时和耗性能的。所以,在查询时都必须要指定类型。而且,尽可能在一类标签(相当于一张表)中进行查询。

比如,下面的语句就会很慢:

MATCH (n) OPTIONAL MATCH (n)-[r]->() RETURN count(r)

若加上类型(标签)就会快很多,比如:

MATCH (n: USER) OPTIONAL MATCH (n)-[r:IP]->() RETURN count(n.name) + count(r)

拆分match子句

当要查询多个类型的点或边时,最好将一个复合的match子句拆分成多个match子句。例如:

  • 非常耗时的查询,如下:
MATCH (b:BANK),(c:COMP) return count(distinct(b)),count(distinct(c))

说明:在1亿个点7.9亿条边的情况下,这条语句非常耗时。explain后发现,该语句会先产生一个非常大的集合,然后在该集合中把b,c两个类型的点分别出来再计算数量。

  • 拆分match子句
MATCH (b:BANK)
with count(b) as banks
MATCH (c:COMP)
return count(c) as comps,banks

说明:该语句会在<5秒返回结果。

增加where子句的复杂度

增加WHERE语句、配合使用AND、OR等加大查询复杂度。

 类似资料: