MVStore在线备份

时间:2018-03-11 02:58:48

标签: h2

关于备份数据库的MVStore文档中的information有点模糊,而且我不熟悉所有的概念和术语,所以我想看看我提出的方法是否合适有道理。

我是Clojure程序员,所以请在这里原谅我的Java:

// db is an MVStore instance
FileStore fs = db.getFileStore();
FileOutputStream fos = java.io.FileOutputStream(pathToBackupFile);
FileChannel outChannel = fos.getChannel();
try {
  db.commit();
  db.setReuseSpace(false);
  ByteBuffer bb = fs.readFully(0, fs.size());
  outChannel.write(bb);
}
finally {
  outChannel.close();
  db.setReuseSpace(true);
}

这里是Clojure的样子,以防我的Java坏了:

(defn backup-db
  [db path-to-backup-file]
  (let [fs (.getFileStore db)
        backup-file (java.io.FileOutputStream. path-to-backup-file)
        out-channel (.getChannel backup-file)]
    (try
      (.commit db)
      (.setReuseSpace db false)
      (let [file-contents (.readFully fs 0 (.size fs))]
        (.write out-channel file-contents))
      (finally
        (.close out-channel)
        (.setReuseSpace db true)))))

我的方法似乎有效,但我想确保我没有遗漏任何东西,或者看看是否有更好的方法。谢谢!

P.S。我使用了H2标签,因为MVStore不存在而且我没有足够的声誉来创建它。

1 个答案:

答案 0 :(得分:0)

docs当前说:

  

即使在写入过程中,也可以随时备份持久数据   操作(在线备份)。为此,自动重复使用磁盘空间   需要首先禁用,以便新数据始终附加在   文件末尾。然后,可以复制文件。文件句柄是   适用于该应用程序。建议使用该实用程序   类FileChannelInputStream可以做到这一点。

FileChannelInputStreamFileChannelOutputStreamjava.nio.FileChannel转换为标准InputStream和OutputStream。 BackupCommand.java中现有的H2代码显示了如何使用它们。我们可以使用Java 9 input.transferTo(output);复制数据来改进它:

    public void backup(MVStore s, File backupFile) throws Exception {
        try {
            s.commit();
            s.setReuseSpace(false);
            try(RandomAccessFile outFile = new java.io.RandomAccessFile(backupFile, "rw");
                FileChannelOutputStream output = new FileChannelOutputStream(outFile.getChannel(), false)){
                    try(FileChannelInputStream input = new FileChannelInputStream(s.getFileStore().getFile(), false)){
                        input.transferTo(output);
                    }
            }
        } finally {
            s.setReuseSpace(true);
        }
    }

请注意,创建FileChannelInputStream时必须传递false来告诉它在关闭流时不要关闭基础文件通道。如果您不这样做,它将关闭您的FileStore试图使用的文件。该代码使用try-with-resource语法来确保正确关闭输出文件。

为了尝试此操作,我签出了mvstore代码,然后修改了TestMVStore以添加一个testBackup()方法,该方法类似于现有的testSimple()代码:

    private void testBackup() throws Exception {
        // write some records like testSimple
        String fileName = getBaseDir() + "/" + getTestName();
        FileUtils.delete(fileName);
        MVStore s = openStore(fileName);
        MVMap<Integer, String> m = s.openMap("data");
        for (int i = 0; i < 3; i++) {
            m.put(i, "hello " + i);
        }

        // create a backup
        String fileNameBackup = getBaseDir() + "/" + getTestName() + ".backup";
        FileUtils.delete(fileNameBackup);
        backup(s, new File(fileNameBackup));

        // this throws if you accidentally close the input channel you get from the store
        s.close();

        // open the backup and verify
        s = openStore(fileNameBackup);
        m = s.openMap("data");

        for (int i = 0; i < 3; i++) {
            assertEquals("hello " + i, m.get(i));
        }
        s.close();
    }

以您的示例为例,您正在读入ByteBuffer,它必须适合内存。使用流transferTo方法使用的内部缓冲区当前(如Java11)设置为8192字节。