教程:在运行中的单个实例中备份和复制单个数据库

本教程提供了有关如何备份单个数据库(在本示例中为版本 3.5)并使用 neo4j-admin copy 命令将其复制到运行中的 4.x Neo4j 独立实例中的详细示例。

neo4j-admin copy 命令可用于清理数据库不一致,压缩存储并升级/迁移数据库(从社区版或企业版)到更高版本的 Neo4j 企业版。由于 neo4j-admin copy 命令不会复制模式存储,因此不需要 顺序路径 的中间步骤。如果定义了模式,则只需通过运行 neo4j-admin copy 操作输出的命令来重新创建它。

请记住,

  • neo4j-admin copy 命令会复制节点 ID,但关系会获得新的 ID。

  • neo4j-admin copy 是一个复制工具,而不是升级或迁移工具。
    它仅复制单个数据库,**不能**应用于 system 数据库。

因此,如果您想保留关系 ID 或升级整个 DBMS,则应遵循 顺序路径

重要的是要注意,neo4j-admin copy 是一个 IOPS 密集型过程。

可以根据以下因素估计 neo4j-admin copy 命令需要多长时间

  • Neo4j 与许多其他数据库一样,以 8K 页面进行 IO。

  • 磁盘制造商提供的磁盘可以处理的 IOPS 的最大值。

例如,如果您的磁盘制造商提供了 5000 IOPS 的最大值,您可以合理地预期每秒最多可以执行 5000 次此类页面操作。因此,您期望的理论最大吞吐量为 40MB/s(或 144 GB/小时)。然后您可以假设,在该 5000 IOPS 磁盘上运行 neo4j-admin copy 的最佳情况是,处理一个 144 GB 的数据库至少需要 1 小时。[1]

但是,重要的是要记住,该过程必须从源数据库读取 144 GB,并且还必须写入目标存储(假设目标存储的大小相当)。此外,在复制过程中,还有内部进程会多次读取/修改/写入存储。因此,再加上 144 GB 的读取和写入,在 5000 IOPS 磁盘上运行 neo4j-admin copy 的最佳情况是,它实际上将**至少需要 3 个小时才能处理一个 144 GB 的数据库**。

最后,还必须考虑在几乎所有云环境中,发布的 IOPS 值可能与实际值不同,或者可能无法持续保持最大可能的 IOPS。本例中实际的处理时间可能远高于 3 小时的估计时间。

本教程将引导您了解检查数据库存储使用情况的基本知识(在本示例中为版本 3.5),执行备份,压缩数据库备份(使用 neo4j-admin copy)并在运行中的 Neo4j 4.x 独立实例中创建它。

检查 3.5 数据库存储使用情况

在备份和复制 3.5 数据库之前,让我们看一下数据库存储的使用情况,并了解在加载、删除然后重新加载数据时它如何变化。

  1. 登录到运行中的 3.5 Neo4j 独立实例的 Neo4j 浏览器,使用以下命令将 100,000 个节点添加到 graph.db 数据库

    FOREACH (x IN RANGE (1,100000) | CREATE (n:Person {name:x}))
  2. Person 节点的 name 属性上创建索引

    CREATE INDEX ON :Person(name)
  3. 使用 dbms.checkpoint() 过程将所有缓存的更新从页面缓存刷新到存储文件。

    CALL dbms.checkpoint()
  4. 在您的终端中,导航到 graph.db 数据库($neo4j_home/data/databases/graph.db)并运行以下命令以检查加载的节点和属性的存储大小。

    ls -alh
    ...
    -rw-r--r--   1 username  staff   1.4M 26 Nov 15:51 neostore.nodestore.db
    -rw-r--r--   1 username  staff   3.9M 26 Nov 15:51 neostore.propertystore.db
    ...

    输出报告节点存储 (neostore.nodestore.db) 和属性存储 (neostore.propertystore.db) 分别占用 1.4M3.9M

  5. 在 Neo4j 浏览器中,删除上面创建的节点,然后再次运行 CALL dbms.checkpoint 以强制进行检查点。

    MATCH (n) DETACH DELETE n
    CALL dbms.checkpoint()
  6. 现在,仅添加一个节点,强制进行检查点,然后重复步骤 4 以查看存储大小是否已更改。

    CREATE (n:Person {name:"John"})
    CALL dbms.checkpoint()

    如果现在检查节点存储和属性存储的大小,它们仍然是 1.4M3.9M,即使数据库仅包含一个节点和一个属性。Neo4j 不会缩小硬盘驱动器上的存储文件。

在生产数据库中,执行了大量的加载/删除操作,结果是存储文件占用了大量的未使用空间。

备份 3.5 数据库

导航到 `/bin` 文件夹,并运行以下命令将数据库备份到目标文件夹。如果要存放备份的文件夹不存在,则需要创建它。在本例中,该文件夹名为 `/tmp/3.5.24`。

./neo4j-admin backup --backup-dir=/tmp/3.5.24 --name=graphdbbackup

有关执行备份和不同命令选项的详细信息,请参见 操作手册 → 执行备份

将 3.5 数据库备份复制到 4.x Neo4j

可以使用 `neo4j-admin copy` 命令回收未使用的空间,并在 4.x 独立实例中创建数据库备份的碎片整理副本。

为了加快复制操作速度,可以使用 `--from-pagecache` 和 `--to-pagecache` 选项指定在读取源和写入目标时要分配多少缓存。作为经验法则,`--to-pagecache` 应在 1-2GB 左右,因为它主要进行顺序写入。`--from-pagecache` 应分配你所能腾出的所有内存,因为 Neo4j 会从源进行随机读取。

  1. 在 4.x Neo4j 独立实例中,导航到 `/bin` 文件夹,并运行以下命令创建 3.5 数据库备份的压缩存储副本。任何不一致的节点、属性和关系都不会被复制到新创建的存储中。

    ./neo4j-admin copy --from-path=/private/tmp/3.5.24/graphdbbackup --to-database=compactdb
    Starting to copy store, output will be saved to:  $neo4j_home/logs/neo4j-admin-copy-2020-11-26.16.07.19.log
    2020-11-26 16:07:19.939+0000 INFO [StoreCopy] ### Copy Data ###
    2020-11-26 16:07:19.940+0000 INFO [StoreCopy] Source: /private/tmp/3.5.24/graphdbbackup (page cache 8m)
    2020-11-26 16:07:19.940+0000 INFO [StoreCopy] Target:  $neo4j_home/data/databases/compactdb (page cache 8m)
    2020-11-26 16:07:19.940+0000 INFO [StoreCopy] Empty database created, will start importing readable data from the source.
    2020-11-26 16:07:21.661+0000 INFO [o.n.i.b.ImportLogic] Import starting
    
    Import starting 2020-11-26 16:07:21.699+0000
      Estimated number of nodes: 50.00 k
      Estimated number of node properties: 50.00 k
      Estimated number of relationships: 0.00
      Estimated number of relationship properties: 50.00 k
      Estimated disk space usage: 2.680MiB
      Estimated required memory usage: 8.598MiB
    
    (1/4) Node import 2020-11-26 16:07:22.220+0000
      Estimated number of nodes: 50.00 k
      Estimated disk space usage: 1.698MiB
      Estimated required memory usage: 8.598MiB
    .......... .......... .......... .......... ..........   5% ∆239ms
    .......... .......... .......... .......... ..........  10% ∆1ms
    .......... .......... .......... .......... ..........  15% ∆1ms
    .......... .......... .......... .......... ..........  20% ∆0ms
    .......... .......... .......... .......... ..........  25% ∆1ms
    .......... .......... .......... .......... ..........  30% ∆0ms
    .......... .......... .......... .......... ..........  35% ∆0ms
    .......... .......... .......... .......... ..........  40% ∆1ms
    .......... .......... .......... .......... ..........  45% ∆0ms
    .......... .......... .......... .......... ..........  50% ∆1ms
    .......... .......... .......... .......... ..........  55% ∆0ms
    .......... .......... .......... .......... .........-  60% ∆51ms
    .......... .......... .......... .......... ..........  65% ∆0ms
    .......... .......... .......... .......... ..........  70% ∆0ms
    .......... .......... .......... .......... ..........  75% ∆1ms
    .......... .......... .......... .......... ..........  80% ∆0ms
    .......... .......... .......... .......... ..........  85% ∆0ms
    .......... .......... .......... .......... ..........  90% ∆1ms
    .......... .......... .......... .......... ..........  95% ∆0ms
    .......... .......... .......... .......... .......... 100% ∆0ms
    
    (2/4) Relationship import 2020-11-26 16:07:22.543+0000
      Estimated number of relationships: 0.00
      Estimated disk space usage: 1006KiB
      Estimated required memory usage: 15.60MiB
    (3/4) Relationship linking 2020-11-26 16:07:22.879+0000
      Estimated required memory usage: 7.969MiB
    (4/4) Post processing 2020-11-26 16:07:23.272+0000
      Estimated required memory usage: 7.969MiB
    -......... .......... .......... .......... ..........   5% ∆356ms
    .......... .......... .......... .......... ..........  10% ∆0ms
    .......... .......... .......... .......... ..........  15% ∆1ms
    .......... .......... .......... .......... ..........  20% ∆0ms
    .......... .......... .......... .......... ..........  25% ∆0ms
    .......... .......... .......... .......... ..........  30% ∆1ms
    .......... .......... .......... .......... ..........  35% ∆0ms
    .......... .......... .......... .......... ..........  40% ∆0ms
    .......... .......... .......... .......... ..........  45% ∆1ms
    .......... .......... .......... .......... ..........  50% ∆0ms
    .......... .......... .......... .......... ..........  55% ∆0ms
    .......... .......... .......... .......... ..........  60% ∆0ms
    .......... .......... .......... .......... ..........  65% ∆1ms
    .......... .......... .......... .......... ..........  70% ∆0ms
    .......... .......... .......... .......... ..........  75% ∆0ms
    .......... .......... .......... .......... ..........  80% ∆0ms
    .......... .......... .......... .......... ..........  85% ∆0ms
    .......... .......... .......... .......... ..........  90% ∆0ms
    .......... .......... .......... .......... ..........  95% ∆1ms
    .......... .......... .......... .......... .......... 100% ∆0ms
    
    
    IMPORT DONE in 2s 473ms.
    Imported:
      1 nodes
      0 relationships
      1 properties
    Peak memory usage: 15.60MiB
    2020-11-26 16:07:24.140+0000 INFO [o.n.i.b.ImportLogic] Import completed successfully, took 2s 473ms. Imported:
      1 nodes
      0 relationships
      1 properties
    2020-11-26 16:07:24.668+0000 INFO [StoreCopy] Import summary: Copying of 100704 records took 4 seconds (25176 rec/s). Unused Records 100703 (99%) Removed Records 0 (0%)
    2020-11-26 16:07:24.669+0000 INFO [StoreCopy] ### Extracting schema ###
    2020-11-26 16:07:24.669+0000 INFO [StoreCopy] Trying to extract schema...
    2020-11-26 16:07:24.920+0000 INFO [StoreCopy] ... found 1 schema definitions. The following can be used to recreate the schema:
    2020-11-26 16:07:24.922+0000 INFO [StoreCopy]
    
    CALL db.createIndex('index_5c0607ad', ['Person'], ['name'], 'native-btree-1.0', {`spatial.cartesian-3d.min`: [-1000000.0, -1000000.0, -1000000.0],`spatial.cartesian.min`: [-1000000.0, -1000000.0],`spatial.wgs-84.min`: [-180.0, -90.0],`spatial.cartesian-3d.max`: [1000000.0, 1000000.0, 1000000.0],`spatial.cartesian.max`: [1000000.0, 1000000.0],`spatial.wgs-84-3d.min`: [-180.0, -90.0, -1000000.0],`spatial.wgs-84-3d.max`: [180.0, 90.0, 1000000.0],`spatial.wgs-84.max`: [180.0, 90.0]})
    2020-11-26 16:07:24.923+0000 INFO [StoreCopy] You have to manually apply the above commands to the database when it is stared to recreate the indexes and constraints. The commands are saved to $neo4j_home/logs/neo4j-admin-copy-2020-11-26.16.07.19.log as well for reference.
  2. 运行以下命令验证数据库是否已成功复制。

    ls -al ../data/databases
    total 0
    drwxr-xr-x@  5 username  staff   160 26 Nov 18:00 .
    drwxr-xr-x@  5 username  staff   160 26 Nov 18:00 ..
    drwxr-xr-x  35 username  staff  1120 26 Nov 17:58 compactdb
    -rw-r--r--   1 username  staff     0 26 Nov 18:00 store_lock
    drwxr-xr-x  33 username  staff  1056 26 Nov 18:00 system

    复制数据库不会自动创建它。因此,如果在 Cypher® Shell 或 Neo4j 浏览器中执行 `SHOW DATABASES`,则它将不可见。

创建压缩备份

现在可以创建复制的数据库,并将它的存储大小与备份数据库的大小进行比较。

  1. 登录到 Cypher Shell 命令行控制台,将活动数据库更改为 `system` (`:USE system;`),并创建 `compactdb` 数据库。有关 Cypher Shell 命令行界面 (CLI) 以及如何使用它的更多信息,请参见 操作手册 → Cypher Shell

    CREATE DATABASE compactdb;
    0 rows available after 145 ms, consumed after another 0 ms
  2. 验证 `compactdb` 数据库是否联机。

    SHOW DATABASES;
    +-------------------------------------------------------------------------------------------------------+
    | name            | address          | role         | requestedStatus | currentStatus | error | default |
    +-------------------------------------------------------------------------------------------------------+
    | "compactdb"     | "localhost:7687" | "standalone" | "online"        | "online"      | ""    | FALSE   |
    | "neo4j"         | "localhost:7687" | "standalone" | "online"        | "online"      | ""    | TRUE    |
    | "system"        | "localhost:7687" | "standalone" | "online"        | "online"      | ""    | FALSE   |
    +-------------------------------------------------------------------------------------------------------+
    
    3 rows available after 10 ms, consumed after another 3 ms
  3. 将活动数据库更改为 `compactdb`,并使用 `neo4j-admin copy` 命令的输出重新创建模式。

    CALL db.createIndex('index_5c0607ad', ['Person'], ['name'], 'native-btree-1.0', {`spatial.cartesian-3d.min`: [-1000000.0, -1000000.0, -1000000.0],`spatial.cartesian.min`: [-1000000.0, -1000000.0],`spatial.wgs-84.min`: [-180.0, -90.0],`spatial.cartesian-3d.max`: [1000000.0, 1000000.0, 1000000.0],`spatial.cartesian.max`: [1000000.0, 1000000.0],`spatial.wgs-84-3d.min`: [-180.0, -90.0, -1000000.0],`spatial.wgs-84-3d.max`: [180.0, 90.0, 1000000.0],`spatial.wgs-84.max`: [180.0, 90.0]});
    +-----------------------------------------------------------------------------------+
    | name             | labels     | properties | providerName       | status          |
    +-----------------------------------------------------------------------------------+
    | "index_5c0607ad" | ["Person"] | ["name"]   | "native-btree-1.0" | "index created" |
    +-----------------------------------------------------------------------------------+
    
    1 row available after 50 ms, consumed after another 5 ms
  4. 验证所有数据是否已成功复制。在本例中,应该有一个节点。

    MATCH (n) RETURN n.name;
    +--------+
    | n.name |
    +--------+
    | "John" |
    +--------+
    
    1 row available after 106 ms, consumed after another 2 ms
  5. 退出 Cypher Shell 命令行控制台。

    :exit;
    
    Bye!
  6. 导航到 `compactdb` 数据库 ($neo4j_home/data/databases/compactdb),并检查复制的节点和属性的存储大小。

    ls -alh
    ...
    -rw-r--r--   1 username  staff   8.0K 26 Nov 17:58 neostore.nodestore.db
    -rw-r--r--   1 username  staff   8.0K 26 Nov 17:58 neostore.propertystore.db
    ...

    输出报告说,节点存储和属性存储现在分别只占用了 8K,而之前分别为 1.4M3.9M


1. 计算基于 `MB/s = (IOPS * B) ÷ 10^6`,其中 `B` 是以字节为单位的块大小;在 Neo4j 的情况下,它是 8000。然后可以从 `(MB/s * 3600) ÷ 1000` 计算出 GB/小时。