空间复用

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 命令行工具添加 10 万个节点,然后查看它们占用多少存储空间。

  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. 使用以下命令向 neo4j 数据库添加 10 万个节点

    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 Browser 中,运行 :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 Browser 中,运行 :sysinfo 检查 neo4j 的总存储大小。

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

    默认情况下,检查点会将页面缓存中所有缓存的更新刷新到存储文件。因此,尽管进行了删除,分配的 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 Browser 中,运行 :sysinfo 检查 neo4jcopy1 的总存储大小。

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

© . All rights reserved.