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

Documentation

上级 f23e48cd
......@@ -26,19 +26,17 @@ MVStore
Similar Projects and Differences to Other Storage Engines</a><br />
<a href="#current_state">
Current State</a><br />
<a href="#building_mvstore">
Building the MVStore Library</a><br />
<a href="#requirements">
Requirements</a><br />
<h2 id="overview">Overview</h2>
<p>
The MVStore is work in progress, and is planned to be the next storage subsystem of H2.
But it can be also directly within an application, without using JDBC or SQL.
</p>
<ul><li>MVStore stands for multi-version store.
</li><li>Each store contains a number of maps (using the <code>java.util.Map</code> interface).
</li><li>The data can be persisted to disk (like a key-value store or a database).
</li><li>Fully in-memory operation is supported.
</li><li>Both file based persistence and in-memory operation are supported.
</li><li>It is intended to be fast, simple to use, and small.
</li><li>Old versions of the data can be read concurrently with all other operations.
</li><li>Transaction are supported (currently only one transaction at a time).
......@@ -55,6 +53,8 @@ The following sample code show how to create a store,
open a map, add some data, and access the current and an old version.
</p>
<pre>
import org.h2.mvstore.*;
// open the store (in-memory if fileName is null)
MVStore s = MVStore.open(fileName);
......@@ -162,7 +162,7 @@ This is possible because internally, each map is organized in the form of a coun
</p><p>
In database terms, a map can be used like a table, where the key of the map is the primary key of the table,
and the value is the row. A map can also represent an index, where the key of the map is the key
of the index, and the value of the map is the primary key of the table (for non-unique indexes
of the index, and the value of the map is the primary key of the table (for non-unique indexes,
the key of the map must also contain the primary key).
</p>
......@@ -174,71 +174,30 @@ A transaction is a number of actions between two versions.
</p><p>
Versions / transactions are not immediately persisted; instead, only the version counter is incremented.
If there is a change after switching to a new version, a snapshot of the old version is kept in memory,
so that the old version can still be read.
so that it can still be read.
</p><p>
Old persisted versions are readable until the old data was explicitly overwritten.
Creating the snapshot is fast: only the pages that are changed after a snapshot are copied.
Creating a snapshot is fast: only the pages that are changed after a snapshot are copied.
This behavior also called COW (copy on write).
</p><p>
Rollback is supported (rollback to any old in-memory version or an old persisted version).
</p>
<h3>Log Structured Storage</h3>
<p>
Currently, store() needs to be called explicitly to save changes.
Changes are buffered in memory, and once enough changes have accumulated
(for example 2 MB), all changes are written in one continuous disk write operation.
But of course, if needed, changes can also be persisted if only little data was changed.
The estimated amount of unsaved changes is tracked.
The plan is to automatically store in a background thread once there are enough changes.
</p><p>
When storing, all changed pages are serialized,
compressed using the LZF algorithm (this can be disabled),
and written sequentially to a free area of the file.
Each such change set is called a chunk.
All parent pages of the changed B-trees are stored in this chunk as well,
so that each chunk also contains the root of each changed map
(which is the entry point to read old data).
There is no separate index: all data is stored as a list of pages.
</p><p>
There are currently two write operations per chunk:
one to store the chunk data (the pages), and one to update the file header
(so it points to the chunk head), but the plan is to write the file header only
once in a while, so it does not slow down opening the store too much.
</p><p>
There is currently no transaction log, no undo log,
and there are no in-place updates (however unused chunks are overwritten).
To efficiently persist very small transactions, the plan is to support a transaction log
where only the deltas is stored, until enough changes have accumulated to persist a chunk.
Old versions are kept and are readable until they are no longer needed.
</p><p>
The plan is to keep all old data for at least one or two minutes (configurable),
so that there are no explicit sync operations required to guarantee data consistency.
To reuse disk space, the chunks with the lowest amount of live data are compacted
(the live data is simply stored again in the next chunk).
To improve data locality and disk space usage, the plan is to automatically defragment and compact data.
</p><p>
Compared to regular databases that use a transaction log, undo log, and main storage area,
the log structured storage is simpler, more flexible, and typically needs less disk operations per change,
as data is only written once instead of twice or 3 times, and because the B-tree pages are
always full (they are stored next to each other) and can be easily compressed.
But temporarily, disk space usage might actually be a bit higher than for a regular database,
as disk space is not immediately re-used (there are no in-place updates).
</p>
<h3>In-Memory Performance and Usage</h3>
<p>
Performance of in-memory operations is comparable with <code>java.util.TreeMap</code>
(many operations are actually faster), but usually slower than <code>java.util.HashMap</code>.
</p><p>
If no file name is specified, the store operates purely in memory.
Except for persisting data, all features are supported in this mode
(multi-versioning, index lookup, R-tree and so on).
</p><p>
The memory overhead for large maps is slightly better than for the regular
map implementations, but there is a higher overhead per map.
For maps with less than 25 entries, the regular map implementations
use less memory on average.
</p><p>
If no file name is specified, the store operates purely in memory.
Except for persisting data, all features are supported in this mode
(multi-versioning, index lookup, R-tree and so on).
If a file name is specified, all operations occur in memory (with the same
performance characteristics) until data is persisted.
</p>
<h3>Pluggable Data Types</h3>
......@@ -290,6 +249,51 @@ that supports concurrent writes
(at the cost of speed if used in a single thread, same as <code>ConcurrentHashMap</code>).
</p>
<h3>Log Structured Storage</h3>
<p>
Currently, <code>store()</code> needs to be called explicitly to save changes.
Changes are buffered in memory, and once enough changes have accumulated
(for example 2 MB), all changes are written in one continuous disk write operation.
But of course, if needed, changes can also be persisted if only little data was changed.
The estimated amount of unsaved changes is tracked.
The plan is to automatically store in a background thread once there are enough changes.
</p><p>
When storing, all changed pages are serialized,
compressed using the LZF algorithm (this can be disabled),
and written sequentially to a free area of the file.
Each such change set is called a chunk.
All parent pages of the changed B-trees are stored in this chunk as well,
so that each chunk also contains the root of each changed map
(which is the entry point to read old data).
There is no separate index: all data is stored as a list of pages.
Per store, the is one additional map that contains the metadata (the list of
maps, where the root page of each map is stored, and the list of chunks).
</p><p>
There are currently two write operations per chunk:
one to store the chunk data (the pages), and one to update the file header
(so it points to the latest chunk), but the plan is to write the file header only
once in a while, in a way that still allows to open a store very quickly.
</p><p>
There is currently no transaction log, no undo log,
and there are no in-place updates (however unused chunks are overwritten).
To efficiently persist very small transactions, the plan is to support a transaction log
where only the deltas is stored, until enough changes have accumulated to persist a chunk.
Old versions are kept and are readable until they are no longer needed.
</p><p>
The plan is to keep all old data for at least one or two minutes (configurable),
so that there are no explicit sync operations required to guarantee data consistency.
To reuse disk space, the chunks with the lowest amount of live data are compacted
(the live data is simply stored again in the next chunk).
To improve data locality and disk space usage, the plan is to automatically defragment and compact data.
</p><p>
Compared to regular databases (that use a transaction log, undo log, and main storage area),
the log structured storage is simpler, more flexible, and typically needs less disk operations per change,
as data is only written once instead of twice or 3 times, and because the B-tree pages are
always full (they are stored next to each other) and can be easily compressed.
But temporarily, disk space usage might actually be a bit higher than for a regular database,
as disk space is not immediately re-used (there are no in-place updates).
</p>
<h3>File System Abstraction, File Locking and Online Backup</h3>
<p>
The file system is pluggable (the same file system abstraction is used as H2 uses).
......@@ -343,15 +347,11 @@ The API as well as the behavior will probably change.
Features may be added and removed (even thought the main features will stay).
</p>
<h2 id="building_mvstore">Building the MVStore Library</h2>
<p>
There is currently no build script.
To test it, run the test within the H2 project in Eclipse or any other IDE.
</p>
<h2 id="requirements">Requirements</h2>
<p>
There are no special requirements.
The MVStore is included in the latest H2 jar file.
</p><p>
There are no special requirements to use it.
The MVStore should run on any JVM as well as on Android
(even thought this was not tested recently).
</p>
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论