空间重用

Neo4j 使用逻辑删除从数据库中删除数据,以实现最大的性能和可扩展性。逻辑删除意味着所有相关记录都被标记为已删除,但它们所占用的空间不会立即返回到操作系统。相反,它随后会被创建数据的交易重用。

将记录标记为已删除需要向事务日志文件写入记录更新命令,就像创建或更新某些内容时一样。因此,在删除大量数据时,这会导致该特定数据库的存储使用量增长,因为 Neo4j 会将所有已删除节点、其属性和关系的记录写入事务日志。

请记住,当对许多节点执行DETACH DELETE时,这些删除操作可能比您预期的占用内存中事务状态和事务日志中更多的空间。

事务最终会从事务日志文件中修剪掉,使日志的存储使用量恢复到预期水平。另一方面,存储文件在删除数据时不会缩小。已删除记录占用的空间保留在存储文件中。在重新使用该空间之前,存储文件是稀疏且碎片化的,但这通常对性能的影响很小。

ID 文件

Neo4j 使用.id文件来管理可以重用的空间。这些文件包含其各自文件中所有已删除记录的 ID 集。记录的 ID 在存储文件中唯一标识它。例如,根据存储格式,所有已删除节点的 ID 都包含在neostore.nodestore.db.idblock.x1.db.id中。

这些.id文件作为与它们交互的写事务的一部分进行维护。当写事务提交删除操作时,记录的 ID 会缓存在内存中。缓冲区跟踪所有重叠的未完成事务。当它们完成后,ID 即可供重用。

缓冲的 ID 作为检查点的一部分刷新到.id文件中。同时,从事务命令中推断出.id文件更改(ID 添加和删除)。这样,恢复过程可确保.id文件始终与其存储文件同步。相同的过程还确保集群数据库具有精确且基于事务的空间重用。

如果要缩小数据库的大小,请不要删除.id文件。存储文件**只能**由 Neo4j 数据库和neo4j-admin工具修改。

回收未使用的空间

您可以使用neo4j-admin database copy命令创建数据库的碎片整理副本。copy命令创建了一个全新的独立数据库。如果要在一个集群中运行该数据库,则必须重新播种现有集群,或从该副本播种一个新的集群。

示例 1. 使用neo4j-admin database copy进行数据库压缩的示例

以下是有关如何检查数据库存储使用情况以及如何回收空间的详细示例。

让我们使用 Cypher Shell 命令行工具添加 100k 个节点,然后查看它们占用了多少存储空间。

  1. 在正在运行的 Neo4j 独立实例中,使用您的凭据登录到 Cypher Shell 命令行工具。

    bin/cypher-shell -u neo4j -p <password>
    Connected to Neo4j at neo4j://localhost:7687 as user neo4j.
    Type :help for a list of available commands or :exit to exit the shell.
    Note that Cypher queries must end with a semicolon.
  2. 使用以下命令将 100k 个节点添加到neo4j数据库

    neo4j@neo4j> foreach (x in range (1,100000) | create (n:testnode1 {id:x}));
    0 rows available after 1071 ms, consumed after another 0 ms
    Added 100000 nodes, Set 100000 properties, Added 100000 labels
  3. 检查已分配的 ID 范围

    neo4j@neo4j> MATCH (n:testnode1) RETURN ID(n) as ID order by ID limit 5;
    +----+
    | ID |
    +----+
    | 0  |
    | 1  |
    | 2  |
    | 3  |
    | 4  |
    +----+
    
    5 rows available after 171 ms, consumed after another 84 ms
  4. 运行call db.checkpoint()过程以强制执行检查点。

    neo4j@neo4j> call db.checkpoint();
    +-----------------------------------+
    | success | message                 |
    +-----------------------------------+
    | TRUE    | "Checkpoint completed." |
    +-----------------------------------+
    
    1 row available after 18 ms, consumed after another 407 ms
  5. 在 Neo4j 浏览器中,运行:sysinfo以检查neo4j的总存储大小。

    存储大小的报告输出为 791.92 KiB,ID 分配:节点 ID 100000,属性 ID 100000。

  6. 删除上面创建的节点。

    neo4j@neo4j> Match (n) detach delete n;
  7. 再次运行call db.checkpoint()过程。

    neo4j@neo4j> call db.checkpoint();
    +-----------------------------------+
    | success | message                 |
    +-----------------------------------+
    | TRUE    | "Checkpoint completed." |
    +-----------------------------------+
    
    1 row available after 18 ms, consumed after another 407 ms
  8. 在 Neo4j 浏览器中,运行:sysinfo以检查neo4j的总存储大小。

    存储大小的报告输出为 31.01 MiB,ID 分配:节点 ID 100000,属性 ID 100000。

    默认情况下,检查点会将 pagecache 中任何缓存的更新刷新到存储文件。因此,已分配的 ID 保持不变,并且存储大小会增加或不更改(如果实例重新启动),尽管已删除。在频繁执行大量加载/删除操作的生产数据库中,结果是存储文件占用大量未使用的空间。

要回收这些未使用的空间,您可以使用neo4j-admin database copy命令创建数据库的碎片整理副本。使用system数据库并在运行命令之前停止neo4j数据库。

  1. 调用neo4j-admin database copy命令以创建neo4j数据库的副本。

    bin/neo4j-admin database copy neo4j neo4jcopy1 --compact-node-store --verbose
    Starting to copy store, output will be saved to: $neo4j_home/logs/neo4j-admin-copy-2020-11-04.11.30.57.log
    2020-10-23 11:40:00.749+0000 INFO [StoreCopy] ### Copy Data ###
    2020-10-23 11:40:00.750+0000 INFO [StoreCopy] Source: $neo4j_home/data/databases/neo4j (page cache 8m) (page cache 8m)
    2020-10-23 11:40:00.750+0000 INFO [StoreCopy] Target: $neo4j_home/data/databases/neo4jcopy1 (page cache 8m)
    2020-10-23 11:40:00.750+0000 INFO [StoreCopy] Empty database created, will start importing readable data from the source.
    2020-10-23 11:40:02.397+0000 INFO [o.n.i.b.ImportLogic] Import starting
    Nodes, started 2020-11-04 11:31:00.088+0000
    [*Nodes:?? 7.969MiB---------------------------------------------------------------------------] 100K ∆ 100K
    Done in 632ms
    Prepare node index, started 2020-11-04 11:31:00.735+0000
    [*DETECT:7.969MiB-----------------------------------------------------------------------------]    0 ∆    0
    Done in 79ms
    Relationships, started 2020-11-04 11:31:00.819+0000
    [*Relationships:?? 7.969MiB-------------------------------------------------------------------]    0 ∆    0
    Done in 37ms
    Node Degrees, started 2020-11-04 11:31:01.162+0000
    [*>:??----------------------------------------------------------------------------------------]    0 ∆    0
    Done in 12ms
    Relationship --> Relationship 1/1, started 2020-11-04 11:31:01.207+0000
    [*>:??----------------------------------------------------------------------------------------]    0 ∆    0
    Done in 0ms
    RelationshipGroup 1/1, started 2020-11-04 11:31:01.232+0000
    [*>:??----------------------------------------------------------------------------------------]    0 ∆    0
    Done in 10ms
    Node --> Relationship, started 2020-11-04 11:31:01.245+0000
    [*>:??----------------------------------------------------------------------------------------]    0 ∆    0
    Done in 10ms
    Relationship <-- Relationship 1/1, started 2020-11-04 11:31:01.287+0000
    [*>:??----------------------------------------------------------------------------------------]    0 ∆    0
    Done in 0ms
    Count groups, started 2020-11-04 11:31:01.549+0000
    [*>:??----------------------------------------------------------------------------------------]    0 ∆    0
    Done in 0ms
    Node --> Group, started 2020-11-04 11:31:01.579+0000
    [*>:??----------------------------------------------------------------------------------------]    0 ∆    0
    Done in 1ms
    Node counts and label index build, started 2020-11-04 11:31:01.986+0000
    [*>:??----------------------------------------------------------------------------------------]    0 ∆    0
    Done in 11ms
    Relationship counts, started 2020-11-04 11:31:02.034+0000
    [*>:??----------------------------------------------------------------------------------------]    0 ∆    0
    Done in 0ms
    
    IMPORT DONE in 3s 345ms.
    Imported:
      0 nodes
      0 relationships
      0 properties
    Peak memory usage: 7.969MiB
    2020-11-04 11:31:02.835+0000 INFO [o.n.i.b.ImportLogic] Import completed successfully, took 3s 345ms. Imported:
      0 nodes
      0 relationships
      0 properties
    2020-11-04 11:31:03.330+0000 INFO [StoreCopy] Import summary: Copying of 100704 records took 5 seconds (20140 rec/s). Unused Records 100704 (100%) Removed Records 0 (0%)
    2020-11-04 11:31:03.330+0000 INFO [StoreCopy] ### Extracting schema ###
    2020-11-04 11:31:03.330+0000 INFO [StoreCopy] Trying to extract schema...
    2020-11-04 11:31:03.338+0000 INFO [StoreCopy] ... found 0 schema definitions.

    此示例生成了一个紧凑且一致的存储(任何不一致的节点、属性、关系都不会复制到新创建的存储中)。

  2. 使用system数据库并创建neo4jcopy1数据库。

    neo4j@system> create database neo4jcopy1;
    0 rows available after 60 ms, consumed after another 0 ms
  3. 验证neo4jcopy1数据库是否联机。

    neo4j@system> show databases;
    +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | name         | type       | aliases | access       | address          | role      | writer | requestedStatus | currentStatus | statusMessage | default | home  | constituents |
    +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    | "neo4j"      | "standard" | []      | "read-write" | "localhost:7687" | "primary" | TRUE   | "offline"       | "offline"     | ""            | TRUE    | TRUE  | []           |
    | "neo4jcopy1" | "standard" | []      | "read-write" | "localhost:7687" | "primary" | TRUE   | "online"        | "online"      | ""            | FALSE   | FALSE | []           |
    | "system"     | "system"   | []      | "read-write" | "localhost:7687" | "primary" | TRUE   | "online"        | "online"      | ""            | FALSE   | FALSE | []           |
    +-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
    
    3 rows available after 2 ms, consumed after another 1 ms
  4. 在 Neo4j 浏览器中,运行:sysinfo以检查neo4jcopy1的总存储大小。

    压缩后存储大小的报告输出为 800.68 KiB,ID 分配:节点 ID 0,属性 ID 0。