提交 dfaf34f5 authored 作者: Thomas Mueller's avatar Thomas Mueller

Inserting LOBs got slower each time the process was restarted.

上级 fbb148e1
...@@ -28,6 +28,7 @@ import org.h2.util.FileUtils; ...@@ -28,6 +28,7 @@ import org.h2.util.FileUtils;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.MathUtils; import org.h2.util.MathUtils;
import org.h2.util.MemoryUtils; import org.h2.util.MemoryUtils;
import org.h2.util.RandomUtils;
import org.h2.util.SmallLRUCache; import org.h2.util.SmallLRUCache;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
...@@ -252,11 +253,12 @@ public class ValueLob extends Value { ...@@ -252,11 +253,12 @@ public class ValueLob extends Value {
private int getNewObjectId(DataHandler handler) throws SQLException { private int getNewObjectId(DataHandler handler) throws SQLException {
String path = handler.getDatabasePath(); String path = handler.getDatabasePath();
int objectId = 0; int objectId = 0;
int lobsPerDir = SysProperties.LOB_FILES_PER_DIRECTORY;
while (true) { while (true) {
String dir = getFileNamePrefix(path, objectId); String dir = getFileNamePrefix(path, objectId);
String[] list = getFileList(handler, dir); String[] list = getFileList(handler, dir);
int fileCount = 0; int fileCount = 0;
boolean[] used = new boolean[SysProperties.LOB_FILES_PER_DIRECTORY]; boolean[] used = new boolean[lobsPerDir];
for (String name : list) { for (String name : list) {
if (name.endsWith(Constants.SUFFIX_DB_FILE)) { if (name.endsWith(Constants.SUFFIX_DB_FILE)) {
name = FileUtils.getFileName(name); name = FileUtils.getFileName(name);
...@@ -269,13 +271,13 @@ public class ValueLob extends Value { ...@@ -269,13 +271,13 @@ public class ValueLob extends Value {
} }
if (id > 0) { if (id > 0) {
fileCount++; fileCount++;
used[id % SysProperties.LOB_FILES_PER_DIRECTORY] = true; used[id % lobsPerDir] = true;
} }
} }
} }
int fileId = -1; int fileId = -1;
if (fileCount < SysProperties.LOB_FILES_PER_DIRECTORY) { if (fileCount < lobsPerDir) {
for (int i = 1; i < SysProperties.LOB_FILES_PER_DIRECTORY; i++) { for (int i = 1; i < lobsPerDir; i++) {
if (!used[i]) { if (!used[i]) {
fileId = i; fileId = i;
break; break;
...@@ -287,26 +289,32 @@ public class ValueLob extends Value { ...@@ -287,26 +289,32 @@ public class ValueLob extends Value {
invalidateFileList(handler, dir); invalidateFileList(handler, dir);
break; break;
} }
if (objectId > Integer.MAX_VALUE / SysProperties.LOB_FILES_PER_DIRECTORY) { if (objectId > Integer.MAX_VALUE / lobsPerDir) {
// this directory path is full: start from zero // this directory path is full: start from zero
// (this can happen only theoretically,
// for example if the random number generator is broken)
objectId = 0; objectId = 0;
dirCounter = RandomUtils.nextInt(lobsPerDir - 1) * lobsPerDir;
} else { } else {
// calculate the directory // calculate the directory
// start with 1 (otherwise we don't know the number of directories) // start with 1 (otherwise we don't know the number of directories)
// it doesn't really matter what directory is used, it might as well be random // it doesn't really matter what directory is used, it might as well be random
// (but that would generate more directories): // (but that would generate more directories):
// int dirId = RandomUtils.nextInt( // int dirId = RandomUtils.nextInt(lobsPerDir - 1) + 1;
// SysProperties.LOB_FILES_PER_DIRECTORY - 1) + 1; int dirId = (dirCounter++ / (lobsPerDir - 1)) + 1;
int dirId = (dirCounter++ / (SysProperties.LOB_FILES_PER_DIRECTORY - 1)) + 1; objectId = objectId * lobsPerDir;
objectId = objectId * SysProperties.LOB_FILES_PER_DIRECTORY; objectId += dirId * lobsPerDir;
objectId += dirId * SysProperties.LOB_FILES_PER_DIRECTORY;
} }
} }
return objectId; return objectId;
} }
/**
* Reset the directory counter as if the process was stopped. This method is
* for debugging only (to simulate stopping a process).
*/
public static void resetDirCounter() {
dirCounter = 0;
}
private void invalidateFileList(DataHandler handler, String dir) { private void invalidateFileList(DataHandler handler, String dir) {
SmallLRUCache<String, String[]> cache = handler.getLobFileListCache(); SmallLRUCache<String, String[]> cache = handler.getLobFileListCache();
if (cache != null) { if (cache != null) {
......
...@@ -26,9 +26,11 @@ import java.util.Random; ...@@ -26,9 +26,11 @@ import java.util.Random;
import org.h2.constant.SysProperties; import org.h2.constant.SysProperties;
import org.h2.store.FileLister; import org.h2.store.FileLister;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.tools.DeleteDbFiles;
import org.h2.util.IOUtils; import org.h2.util.IOUtils;
import org.h2.util.ObjectUtils; import org.h2.util.ObjectUtils;
import org.h2.util.StringUtils; import org.h2.util.StringUtils;
import org.h2.value.ValueLob;
/** /**
* Tests LOB and CLOB data types. * Tests LOB and CLOB data types.
...@@ -45,6 +47,7 @@ public class TestLob extends TestBase { ...@@ -45,6 +47,7 @@ public class TestLob extends TestBase {
} }
public void test() throws Exception { public void test() throws Exception {
testAddLobRestart();
testLobServerMemory(); testLobServerMemory();
if (config.memory) { if (config.memory) {
return; return;
...@@ -74,6 +77,27 @@ public class TestLob extends TestBase { ...@@ -74,6 +77,27 @@ public class TestLob extends TestBase {
deleteDb("lob"); deleteDb("lob");
} }
private void testAddLobRestart() throws SQLException {
DeleteDbFiles.execute("memFS:", "lob", true);
Connection conn = org.h2.Driver.load().connect("jdbc:h2:memFS:lob", null);
Statement stat = conn.createStatement();
stat.execute("create table test(d blob)");
stat.execute("set MAX_LENGTH_INPLACE_LOB 1");
PreparedStatement prep = conn.prepareCall("insert into test values('0000')");
// long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
// if (i % 1000 == 0) {
// long now = System.currentTimeMillis();
// System.out.println(i + " " + (now - start));
// start = now;
// }
prep.execute();
ValueLob.resetDirCounter();
}
conn.close();
DeleteDbFiles.execute("memFS:", "lob", true);
}
private void testLobUpdateMany() throws SQLException { private void testLobUpdateMany() throws SQLException {
deleteDb("lob"); deleteDb("lob");
Connection conn = getConnection("lob"); Connection conn = getConnection("lob");
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论