Neo4jのメモリ設定をざっくり把握する

[こちら](https://qiita.com/stmtok/items/49f2002c8baafd032e05)に書いていたものを個別に使いたい場面があったので、分割しました。
(例の中で推奨設定の取得にdockerイメージを使用していますが、neo4jをローカルにインストールしている人は直接neo4j-adminコマンドを使用できます。)

#Neo4jのメモリ設定について
[公式ドキュメント] (https://neo4j.com/docs/operations-manual/current/performance/memory-configuration/)や[Neo4j Performance Tuning] (https://neo4j.com/developer/guide-performance-tuning/)に詳しく書いてあるので、詳しくはそちらを参照していただくとして、ざっくりと把握することを目的としています。

##neo4jで考慮するメモリ空間

* OS memory および Lucene index cache
* Page cache
* Heap space

まず、neo4jのホストOSが他のプロセスの実行に必要なメモリを確保しておく必要があります。また、neo4jは[Apache Lucene] (https://lucene.apache.org/)をインデックスで使用しており、Lucene index cacheのためにメモリを確保しておく必要があります。これらのメモリ容量はNeo4j側から制御することができません。
Page cacheはNeo4jデータおよびNative indexesのキャッシュに使用され、低速なディスクアクセスを回避するために用いられます。そのため、全データ分の容量確保が理想的です。
Heap spaceはNeo4jの実行に必要なメモリ空間です。設定では`dbms.memory.heap.inital_size`と`dbms.memory.heap.max_size`が提供されていますが、これらの数値は同じものを指定することが推奨されています。同じ値を設定することにより、無駄なガーベッジコレクションを回避することができるためです。

## メモリの推奨設定を確認する
`neo4j-admin`コマンドを使用することによって推奨設定値を確認することができます。

```
docker run --rm neo4j:latest neo4j-admin memrec

# Memory settings recommendation from neo4j-admin memrec:
#
# Assuming the system is dedicated to running Neo4j and has 2500m of memory,
# we recommend a heap size of around 851m, and a page cache of around 512m,
# and that about 1100m is left for the operating system, and the native memory
# needed by Lucene and Netty.
#
# Tip: If the indexing storage use is high, e.g. there are many indexes or most
# data indexed, then it might advantageous to leave more memory for the
# operating system.
#
# Tip: The more concurrent transactions your workload has and the more updates
# they do, the more heap memory you will need. However, don't allocate more
# than 31g of heap, since this will disable pointer compression, also known as
# "compressed oops", in the JVM and make less effective use of the heap.
#
# Tip: Setting the initial and the max heap size to the same value means the
# JVM will never need to change the heap size. Changing the heap size otherwise
# involves a full GC, which is desirable to avoid.
#
# Based on the above, the following memory settings are recommended:
dbms.memory.heap.initial_size=851m
dbms.memory.heap.max_size=851m
dbms.memory.pagecache.size=512m
```
上記のコマンドではneo4j-adminが実行されたマシンのメモリを元に、メモリの振り分けの推奨値を表示します。

また、マシンのメモリサイズを指定して実行することもできます。

```
docker run --rm neo4j:latest neo4j-admin memrec --memory=10G
...
...
dbms.memory.heap.initial_size=4g
dbms.memory.heap.max_size=4g
dbms.memory.pagecache.size=3g
```

さらに、Neo4jのサイズおよびLucene indexのサイズを確認することができます。

```
docker run --rm neo4j:latest neo4j-admin memrec --database=graph.db
...
...
# Lucene indexes: 6690m
# Data volume and native indexes: 17050m
```
上記の例ではLucene indexesが6.7GB、Neo4jのサイズが17GBであることがわかります。

## メモリ設計
上記の例を用いてメモリ設計を行ってみます。まず確保するべきメモリを再確認します

* OS memory 
* Lucene index cache
* Page cache
* Heap space

OS memoryは[公式ドキュメント] (https://neo4j.com/docs/operations-manual/current/performance/memory-configuration/)に書いてあるとおり、ほとんどの場合で1GBもあれば十分です。
Heap Sizeは[compressed oops] (https://www.oracle.com/technetwork/jp/articles/java/compressedoops-427542-ja.html)で知られているように最大が31GBです。また[Neo4j Performance Tuning] (https://neo4j.com/developer/guide-performance-tuning/)には8GB~16GBで十分であると書かれているので、今回は8GBを採用します。
そしてneo4j-adminコマンドで確認したLucene index cacheは6.7GB、データサイズが17GBです。
データサイズはデータの今後の成長を見込んで、例えば1.2倍のサイズ`17GB x 1.2 = 20.4GB`を確保するとします。

すると最終的に必要なメモリサイズは`1GB + 6.7GB + 8GB + 20.4GB = 36.1GB`となります。

今回はかなり乱暴な見積もりを行なっています。Heap Sizeは実行するクエリ内容によってはもっと少なくとも快適に動作しますし、データサイズに関しても、私が確認したところでは、140万ノード（平均20プロパティ）、320万リンク程度でデータサイズが1.3GB程度です。

適切なメモリ設定を行うことで相当のパフォーマンス向上が見込まれます。
どこにコストがかかっているのかの計測を行い、限られたリソース内でも適切にメモリ設計を行なってNeo4jのパフォーマンスを引き出していきましょう。