关于备份数据库的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不存在而且我没有足够的声誉来创建它。
答案 0 :(得分:0)
docs当前说:
即使在写入过程中,也可以随时备份持久数据 操作(在线备份)。为此,自动重复使用磁盘空间 需要首先禁用,以便新数据始终附加在 文件末尾。然后,可以复制文件。文件句柄是 适用于该应用程序。建议使用该实用程序 类FileChannelInputStream可以做到这一点。
类FileChannelInputStream
和FileChannelOutputStream
将java.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字节。