提交 49dfa28f authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore: additional compact feature (shrink file size)

上级 1df6f5fc
...@@ -148,7 +148,7 @@ public class FreeSpaceBitSet { ...@@ -148,7 +148,7 @@ public class FreeSpaceBitSet {
* @return the fill rate (0 - 100) * @return the fill rate (0 - 100)
*/ */
public int getFillRate() { public int getFillRate() {
int total = set.size(), count = 0; int total = set.length(), count = 0;
for (int i = 0; i < total; i++) { for (int i = 0; i < total; i++) {
if (set.get(i)) { if (set.get(i)) {
count++; count++;
...@@ -157,7 +157,7 @@ public class FreeSpaceBitSet { ...@@ -157,7 +157,7 @@ public class FreeSpaceBitSet {
if (count == 0) { if (count == 0) {
return 0; return 0;
} }
return Math.min(1, (int) (100L * count / total)); return Math.max(1, (int) (100L * count / total));
} }
/** /**
......
...@@ -48,6 +48,8 @@ TODO: ...@@ -48,6 +48,8 @@ TODO:
Documentation Documentation
- rolling docs review: at "Transactions" - rolling docs review: at "Transactions"
- better document that writes are in background thread
- better document how to do non-unique indexes
TestMVStoreDataLoss TestMVStoreDataLoss
...@@ -116,6 +118,11 @@ MVStore: ...@@ -116,6 +118,11 @@ MVStore:
- rename setStoreVersion to setDataVersion or similar - rename setStoreVersion to setDataVersion or similar
- to save space for small chunks, combine the last partial - to save space for small chunks, combine the last partial
block with the header block with the header
- off-heap storage (with lower default retention time)
- object value cache for default serialization
- temporary file storage
- simple rollback method (rollback to last committed version)
- MVMap to implement SortedMap, then NavigableMap
*/ */
...@@ -1004,9 +1011,7 @@ public class MVStore { ...@@ -1004,9 +1011,7 @@ public class MVStore {
DataUtils.writeFully(file, filePos, buff); DataUtils.writeFully(file, filePos, buff);
fileSize = Math.max(fileSize, filePos + buff.position()); fileSize = Math.max(fileSize, filePos + buff.position());
if (buff.capacity() <= 4 * 1024 * 1024) { releaseWriteBuffer(buff);
writeBuffer = buff;
}
// overwrite the header if required // overwrite the header if required
if (!storeAtEndOfFile) { if (!storeAtEndOfFile) {
...@@ -1049,6 +1054,18 @@ public class MVStore { ...@@ -1049,6 +1054,18 @@ public class MVStore {
return buff; return buff;
} }
/**
* Release a buffer for writing. This caller must synchronize on the store
* before calling the method and until after using the buffer.
*
* @param buff the buffer than can be re-used
*/
private void releaseWriteBuffer(ByteBuffer buff) {
if (buff.capacity() <= 4 * 1024 * 1024) {
writeBuffer = buff;
}
}
private boolean canOverwriteChunk(Chunk c, long time) { private boolean canOverwriteChunk(Chunk c, long time) {
if (c.time + retentionTime > time) { if (c.time + retentionTime > time) {
return false; return false;
...@@ -1198,44 +1215,38 @@ public class MVStore { ...@@ -1198,44 +1215,38 @@ public class MVStore {
} }
/** /**
* Compact the store by moving all chunks. * Compact the store by moving all chunks next to each other, if there is
* free space between chunks. This might temporarily double the file size.
* *
* @return if anything was written * @return if anything was written
*/ */
public synchronized boolean compactFully() { public synchronized boolean compactMoveChunks() {
checkOpen(); checkOpen();
if (chunks.size() == 0) { if (chunks.size() == 0) {
// nothing to do // nothing to do
return false; return false;
} }
if (freeSpace.getFillRate() == 100) { if (freeSpace.getFillRate() == 100) {
boolean allFull = true; return false;
for (Chunk c : chunks.values()) {
if (c.maxLength == c.maxLengthLive) {
allFull = false;
break;
}
}
if (allFull) {
return false;
}
} }
long firstFree = freeSpace.getFirstFree(); long firstFree = freeSpace.getFirstFree();
ArrayList<Chunk> move = New.arrayList(); ArrayList<Chunk> move = New.arrayList();
for (Chunk c : chunks.values()) { for (Chunk c : chunks.values()) {
if (c.start < firstFree) { if (c.start > firstFree) {
move.add(c); move.add(c);
} }
} }
ByteBuffer buff = getWriteBuffer();
for (Chunk c : move) { for (Chunk c : move) {
ByteBuffer buff = getWriteBuffer();
int length = MathUtils.roundUpInt(c.length, BLOCK_SIZE) + BLOCK_SIZE; int length = MathUtils.roundUpInt(c.length, BLOCK_SIZE) + BLOCK_SIZE;
buff = DataUtils.ensureCapacity(buff, length); buff = DataUtils.ensureCapacity(buff, length);
buff.limit(length);
DataUtils.readFully(file, c.start, buff); DataUtils.readFully(file, c.start, buff);
long pos = getFileSizeUsed(); long pos = getFileSizeUsed();
freeSpace.markUsed(pos, length); freeSpace.markUsed(pos, length);
buff.position(0); freeSpace.free(c.start, length);
c.start = pos; c.start = pos;
buff.position(0);
c.writeHeader(buff); c.writeHeader(buff);
buff.position(buff.limit() - BLOCK_SIZE); buff.position(buff.limit() - BLOCK_SIZE);
byte[] header = getFileHeaderBytes(); byte[] header = getFileHeaderBytes();
...@@ -1243,12 +1254,48 @@ public class MVStore { ...@@ -1243,12 +1254,48 @@ public class MVStore {
// fill the header with zeroes // fill the header with zeroes
buff.put(new byte[BLOCK_SIZE - header.length]); buff.put(new byte[BLOCK_SIZE - header.length]);
buff.position(0); buff.position(0);
fileWriteCount++;
DataUtils.writeFully(file, pos, buff);
fileSize = Math.max(fileSize, pos + buff.position());
releaseWriteBuffer(buff);
meta.put("chunk." + c.id, c.asString());
}
boolean oldReuse = reuseSpace;
// update the metadata (store at the end of the file)
reuseSpace = false;
store();
// now re-use the empty space
reuseSpace = true;
for (Chunk c : move) {
ByteBuffer buff = getWriteBuffer();
int length = MathUtils.roundUpInt(c.length, BLOCK_SIZE) + BLOCK_SIZE;
buff = DataUtils.ensureCapacity(buff, length);
DataUtils.readFully(file, c.start, buff);
long pos = freeSpace.allocate(length);
freeSpace.free(c.start, length);
buff.position(0);
c.start = pos;
c.writeHeader(buff);
buff.position(buff.limit() - BLOCK_SIZE);
byte[] header = getFileHeaderBytes();
buff.put(header);
// fill the header with zeroes
buff.put(new byte[BLOCK_SIZE - header.length]);
buff.limit(length); buff.limit(length);
// file.write(buff); buff.position(0);
fileWriteCount++;
DataUtils.writeFully(file, pos, buff);
fileSize = Math.max(fileSize, pos + buff.position());
releaseWriteBuffer(buff);
meta.put("chunk." + c.id, c.asString());
} }
int todo; // update the metadata (within the file)
return false; store();
reuseSpace = oldReuse;
return true;
} }
/** /**
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论