Unverified 提交 fc0670d5 authored 作者: Andrei Tokar's avatar Andrei Tokar 提交者: GitHub

Merge pull request #1547 from h2database/mvstore-misc

Speedup unused chunks collection
...@@ -1349,16 +1349,14 @@ public class MVStore { ...@@ -1349,16 +1349,14 @@ public class MVStore {
if (time >= lastFreeUnusedChunks + freeDelay) { if (time >= lastFreeUnusedChunks + freeDelay) {
// set early in case it fails (out of memory or so) // set early in case it fails (out of memory or so)
lastFreeUnusedChunks = time; lastFreeUnusedChunks = time;
freeUnusedChunks(); freeUnusedChunks(true);
// set it here as well, to avoid calling it often if it was slow
lastFreeUnusedChunks = getTimeSinceCreation();
} }
} }
private void freeUnusedChunks() { private void freeUnusedChunks(boolean fast) {
assert storeLock.isHeldByCurrentThread(); assert storeLock.isHeldByCurrentThread();
if (lastChunk != null && reuseSpace) { if (lastChunk != null && reuseSpace) {
Set<Integer> referenced = collectReferencedChunks(); Set<Integer> referenced = collectReferencedChunks(fast);
long time = getTimeSinceCreation(); long time = getTimeSinceCreation();
for (Iterator<Chunk> iterator = chunks.values().iterator(); iterator.hasNext(); ) { for (Iterator<Chunk> iterator = chunks.values().iterator(); iterator.hasNext(); ) {
...@@ -1383,54 +1381,82 @@ public class MVStore { ...@@ -1383,54 +1381,82 @@ public class MVStore {
} }
} }
} }
// set it here, to avoid calling it often if it was slow
lastFreeUnusedChunks = getTimeSinceCreation();
} }
} }
private Set<Integer> collectReferencedChunks() { /**
* Collect ids for chunks that are no longer in use.
* @param fast if true, simplified version is used, which assumes that recent chunks
* are still in-use and do not scan recent versions of the store.
* Also is this case only oldest available version of the store is scanned.
*/
private Set<Integer> collectReferencedChunks(boolean fast) {
assert lastChunk != null;
final ThreadPoolExecutor executorService = new ThreadPoolExecutor(10, 10, 10L, TimeUnit.SECONDS, final ThreadPoolExecutor executorService = new ThreadPoolExecutor(10, 10, 10L, TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(keysPerPage + 1)); new ArrayBlockingQueue<Runnable>(keysPerPage + 1));
final AtomicInteger executingThreadCounter = new AtomicInteger(0); final AtomicInteger executingThreadCounter = new AtomicInteger(0);
try { try {
ChunkIdsCollector collector = new ChunkIdsCollector(meta.getId()); ChunkIdsCollector collector = new ChunkIdsCollector(meta.getId());
Set<Long> inspectedRoots = new HashSet<>();
long pos = lastChunk.metaRootPos;
inspectedRoots.add(pos);
collector.visit(pos, executorService, executingThreadCounter);
long oldestVersionToKeep = getOldestVersionToKeep(); long oldestVersionToKeep = getOldestVersionToKeep();
MVMap.RootReference rootReference = meta.getRoot(); MVMap.RootReference rootReference = meta.getRoot();
do { if (fast) {
Page rootPage = rootReference.root; MVMap.RootReference previous;
pos = rootPage.getPos(); while (rootReference.version >= oldestVersionToKeep && (previous = rootReference.previous) != null) {
if (!rootPage.isSaved()) { rootReference = previous;
collector.setMapId(meta.getId());
collector.visit(rootPage, executorService, executingThreadCounter);
} else if (inspectedRoots.add(pos)) {
collector.setMapId(meta.getId());
collector.visit(pos, executorService, executingThreadCounter);
} }
inspectVersion(rootReference, collector, executorService, executingThreadCounter, null);
for (Cursor<String, String> c = new Cursor<>(rootPage, "root."); c.hasNext();) { Page rootPage = rootReference.root;
String key = c.next(); long pos = rootPage.getPos();
assert key != null; assert rootPage.isSaved();
if (!key.startsWith("root.")) { int chunkId = DataUtils.getPageChunkId(pos);
break; while (++chunkId <= lastChunk.id) {
} collector.registerChunk(chunkId);
pos = DataUtils.parseHexLong(c.getValue());
if (DataUtils.isPageSaved(pos) && inspectedRoots.add(pos)) {
// to allow for something like "root.tmp.123" to be
// processed
int mapId = DataUtils.parseHexInt(key.substring(key.lastIndexOf('.') + 1));
collector.setMapId(mapId);
collector.visit(pos, executorService, executingThreadCounter);
}
} }
} while (rootReference.version >= oldestVersionToKeep && (rootReference = rootReference.previous) != null); } else {
Set<Long> inspectedRoots = new HashSet<>();
do {
inspectVersion(rootReference, collector, executorService, executingThreadCounter, inspectedRoots);
} while (rootReference.version >= oldestVersionToKeep && (rootReference = rootReference.previous) != null);
}
return collector.getReferenced(); return collector.getReferenced();
} finally { } finally {
executorService.shutdownNow(); executorService.shutdownNow();
} }
} }
private void inspectVersion(MVMap.RootReference rootReference, ChunkIdsCollector collector,
ThreadPoolExecutor executorService,
AtomicInteger executingThreadCounter,
Set<Long> inspectedRoots) {
Page rootPage = rootReference.root;
long pos = rootPage.getPos();
if (rootPage.isSaved()) {
if (inspectedRoots != null && !inspectedRoots.add(pos)) {
return;
}
collector.setMapId(meta.getId());
collector.visit(pos, executorService, executingThreadCounter);
}
for (Cursor<String, String> c = new Cursor<>(rootPage, "root."); c.hasNext(); ) {
String key = c.next();
assert key != null;
if (!key.startsWith("root.")) {
break;
}
pos = DataUtils.parseHexLong(c.getValue());
assert DataUtils.isPageSaved(pos);
if (inspectedRoots == null || inspectedRoots.add(pos)) {
// to allow for something like "root.tmp.123" to be processed
int mapId = DataUtils.parseHexInt(key.substring(key.lastIndexOf('.') + 1));
collector.setMapId(mapId);
collector.visit(pos, executorService, executingThreadCounter);
}
}
}
final class ChunkIdsCollector { final class ChunkIdsCollector {
/** really a set */ /** really a set */
...@@ -1769,7 +1795,7 @@ public class MVStore { ...@@ -1769,7 +1795,7 @@ public class MVStore {
boolean oldReuse = reuseSpace; boolean oldReuse = reuseSpace;
try { try {
retentionTime = -1; retentionTime = -1;
freeUnusedChunks(); freeUnusedChunks(false);
if (fileStore.getFillRate() <= targetFillRate) { if (fileStore.getFillRate() <= targetFillRate) {
long start = fileStore.getFirstFree() / BLOCK_SIZE; long start = fileStore.getFirstFree() / BLOCK_SIZE;
ArrayList<Chunk> move = findChunksToMove(start, moveSize); ArrayList<Chunk> move = findChunksToMove(start, moveSize);
...@@ -2064,7 +2090,7 @@ public class MVStore { ...@@ -2064,7 +2090,7 @@ public class MVStore {
} }
} }
meta.rewrite(set); meta.rewrite(set);
freeUnusedChunks(); freeUnusedChunks(false);
commit(); commit();
} }
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论