0
# Embedded MVStore
1
2
MVStore is H2 Database's advanced storage engine that can be used independently as a high-performance, persistent key-value store. It provides ACID transactions, multi-version concurrency control (MVCC), and various specialized data structures including maps, trees, and spatial indexes.
3
4
## Core MVStore Classes
5
6
### MVStore
7
8
The main store class that manages persistent key-value maps with ACID transactions.
9
10
```java { .api }
11
public final class MVStore implements AutoCloseable {
12
// Static factory methods
13
public static MVStore open(String fileName);
14
public static Builder builder();
15
16
// Map management
17
public <K, V> MVMap<K, V> openMap(String name);
18
public <M extends MVMap<K, V>, K, V> M openMap(String name, MVMap.MapBuilder<M, K, V> builder);
19
public <K, V> MVMap<K, V> openMap(String name, DataType keyType, DataType valueType);
20
public void removeMap(String name);
21
public Set<String> getMapNames();
22
23
// Store information
24
public Map<String, String> getLayoutMap();
25
public long getCurrentVersion();
26
public int getFileStore().getReadCount();
27
public int getFileStore().getWriteCount();
28
public long getFileStore().size();
29
30
// Transaction and persistence
31
public void commit();
32
public long commitAndSave();
33
public void rollback();
34
public void save();
35
36
// Store lifecycle
37
public void close();
38
public void closeImmediately();
39
public boolean isClosed();
40
public MVStoreException getPanicException();
41
42
// Configuration
43
public int getCacheSizeUsed();
44
public void setCacheSize(int mb);
45
public void setVersionsToKeep(int count);
46
public void setAutoCommitDelay(int millis);
47
}
48
```
49
50
**Usage Examples:**
51
52
```java
53
// Create/open file-based store
54
MVStore store = MVStore.open("data.mv");
55
56
// Create in-memory store
57
MVStore store = new MVStore.Builder().open();
58
59
// Open map
60
MVMap<String, String> map = store.openMap("users");
61
62
// Store data
63
map.put("user1", "Alice");
64
map.put("user2", "Bob");
65
66
// Commit changes
67
store.commit();
68
69
// Close store
70
store.close();
71
```
72
73
### MVStore.Builder
74
75
Builder pattern for configuring MVStore instances.
76
77
```java { .api }
78
public static class Builder {
79
public Builder fileName(String fileName);
80
public Builder cacheSize(int mb);
81
public Builder autoCommitDisabled();
82
public Builder autoCommitBufferSize(int kb);
83
public Builder compress();
84
public Builder compressHigh();
85
public Builder encryptionKey(char[] encryptionKey);
86
public Builder readOnly();
87
public Builder cacheConcurrency(int concurrency);
88
public Builder backgroundExceptionHandler(Thread.UncaughtExceptionHandler handler);
89
90
public MVStore open();
91
}
92
```
93
94
**Usage Examples:**
95
96
```java
97
// Configure store with builder
98
MVStore store = new MVStore.Builder()
99
.fileName("mydata.mv")
100
.cacheSize(64) // 64 MB cache
101
.compress() // Enable compression
102
.autoCommitBufferSize(1024) // 1 MB auto-commit buffer
103
.open();
104
105
// In-memory compressed store
106
MVStore memStore = new MVStore.Builder()
107
.compress()
108
.cacheSize(32)
109
.open();
110
111
// Read-only store
112
MVStore readOnlyStore = new MVStore.Builder()
113
.fileName("readonly.mv")
114
.readOnly()
115
.open();
116
117
// Encrypted store
118
char[] password = "mySecretPassword".toCharArray();
119
MVStore encryptedStore = new MVStore.Builder()
120
.fileName("encrypted.mv")
121
.encryptionKey(password)
122
.open();
123
```
124
125
### MVMap
126
127
Persistent map implementation with MVCC support.
128
129
```java { .api }
130
public class MVMap<K, V> {
131
// Basic map operations
132
public V put(K key, V value);
133
public V get(Object key);
134
public V remove(Object key);
135
public boolean containsKey(Object key);
136
public boolean containsValue(Object value);
137
public boolean isEmpty();
138
public long size();
139
public void clear();
140
141
// Bulk operations
142
public void putAll(Map<? extends K, ? extends V> m);
143
public Set<K> keySet();
144
public Collection<V> values();
145
public Set<Map.Entry<K, V>> entrySet();
146
147
// Versioned operations
148
public V put(K key, V value, boolean tryReplace);
149
public boolean replace(K key, V oldValue, V newValue);
150
public V putIfAbsent(K key, V value);
151
public boolean remove(Object key, Object value);
152
153
// Range operations
154
public K firstKey();
155
public K lastKey();
156
public K lowerKey(K key);
157
public K floorKey(K key);
158
public K ceilingKey(K key);
159
public K higherKey(K key);
160
161
// Cursor operations
162
public Cursor<K, V> cursor(K from);
163
public Cursor<K, V> cursor(K from, K to, boolean includeKey);
164
165
// Versioning
166
public MVMap<K, V> openVersion(long version);
167
public long getVersion();
168
169
// Map information
170
public String getName();
171
public DataType getKeyType();
172
public DataType getValueType();
173
public boolean isReadOnly();
174
}
175
```
176
177
**Usage Examples:**
178
179
```java
180
MVStore store = MVStore.open("data.mv");
181
MVMap<Integer, String> map = store.openMap("customers");
182
183
// Basic operations
184
map.put(1, "Alice Johnson");
185
map.put(2, "Bob Smith");
186
String customer = map.get(1); // "Alice Johnson"
187
188
// Conditional operations
189
String oldValue = map.putIfAbsent(3, "Charlie Brown"); // null if inserted
190
boolean replaced = map.replace(1, "Alice Johnson", "Alice Williams");
191
192
// Range queries
193
Integer firstId = map.firstKey(); // 1
194
Integer lastId = map.lastKey(); // 3
195
196
// Iteration
197
for (Map.Entry<Integer, String> entry : map.entrySet()) {
198
System.out.println(entry.getKey() + ": " + entry.getValue());
199
}
200
201
// Cursors for efficient range iteration
202
Cursor<Integer, String> cursor = map.cursor(2); // Start from key 2
203
while (cursor.hasNext()) {
204
System.out.println(cursor.next());
205
}
206
207
store.close();
208
```
209
210
### Transaction Support
211
212
MVStore provides built-in transaction support with MVCC.
213
214
```java { .api }
215
// Transaction methods in MVStore
216
public long getCurrentVersion();
217
public void rollback();
218
public void rollbackTo(long version);
219
220
// Versioned map access
221
public MVMap<K, V> openVersion(long version);
222
```
223
224
**Usage Examples:**
225
226
```java
227
MVStore store = MVStore.open("transactional.mv");
228
MVMap<String, Integer> accounts = store.openMap("accounts");
229
230
// Initial balances
231
accounts.put("alice", 1000);
232
accounts.put("bob", 500);
233
store.commit();
234
235
// Start transaction
236
long version = store.getCurrentVersion();
237
try {
238
// Transfer money
239
int aliceBalance = accounts.get("alice");
240
int bobBalance = accounts.get("bob");
241
242
if (aliceBalance >= 100) {
243
accounts.put("alice", aliceBalance - 100);
244
accounts.put("bob", bobBalance + 100);
245
store.commit(); // Commit transaction
246
} else {
247
store.rollbackTo(version); // Rollback on insufficient funds
248
}
249
} catch (Exception e) {
250
store.rollbackTo(version); // Rollback on error
251
throw e;
252
}
253
254
store.close();
255
```
256
257
## Advanced Data Types
258
259
### MVStore.Builder with Custom Data Types
260
261
```java { .api }
262
// Custom data type configuration
263
public <K, V> MVMap<K, V> openMap(String name, DataType keyType, DataType valueType);
264
265
public static class MapBuilder<M extends MVMap<K, V>, K, V> {
266
public MapBuilder<M, K, V> keyType(DataType keyType);
267
public MapBuilder<M, K, V> valueType(DataType valueType);
268
public M create();
269
}
270
```
271
272
**Available Data Types:**
273
274
```java { .api }
275
public interface DataType {
276
// Built-in data types
277
DataType STRING_TYPE = new StringDataType();
278
DataType BYTE_ARRAY_TYPE = new ByteArrayDataType();
279
DataType OBJECT_TYPE = new ObjectDataType();
280
281
// Type operations
282
int compare(Object a, Object b);
283
int getMemory(Object obj);
284
void write(WriteBuffer buff, Object obj);
285
Object read(ByteBuffer buff);
286
}
287
```
288
289
**Usage Examples:**
290
291
```java
292
// String keys, Object values
293
MVMap<String, Object> objectMap = store.openMap("objects",
294
new StringDataType(), new ObjectDataType());
295
296
// Custom serializable objects
297
MVMap<String, Customer> customerMap = store.openMap("customers",
298
new StringDataType(), new ObjectDataType());
299
300
customerMap.put("cust1", new Customer("Alice", "alice@example.com"));
301
Customer customer = customerMap.get("cust1");
302
```
303
304
## Specialized Maps
305
306
### RTRee Maps (Spatial Indexing)
307
308
MVStore includes spatial indexing support through R-tree implementation.
309
310
```java { .api }
311
public class MVRTreeMap<V> extends MVMap<Spatial, V> {
312
public static <V> MVRTreeMap<V> create(MVStore store, String name, DataType valueType);
313
314
// Spatial queries
315
public Iterator<V> findIntersectingKeys(Spatial bounds);
316
public Iterator<V> findContainedKeys(Spatial bounds);
317
318
// Spatial operations
319
public V add(Spatial key, V value);
320
public boolean remove(Spatial key, V value);
321
}
322
```
323
324
**Usage Examples:**
325
326
```java
327
import org.h2.mvstore.rtree.*;
328
329
MVStore store = MVStore.open("spatial.mv");
330
MVRTreeMap<String> rtree = MVRTreeMap.create(store, "locations", new StringDataType());
331
332
// Add spatial objects (rectangles defined by min/max x,y coordinates)
333
rtree.add(new SpatialKey(0, 0, 10, 10), "Location A");
334
rtree.add(new SpatialKey(5, 5, 15, 15), "Location B");
335
rtree.add(new SpatialKey(20, 20, 30, 30), "Location C");
336
337
// Spatial queries
338
SpatialKey searchArea = new SpatialKey(0, 0, 12, 12);
339
Iterator<String> results = rtree.findIntersectingKeys(searchArea);
340
while (results.hasNext()) {
341
System.out.println("Found: " + results.next());
342
}
343
344
store.close();
345
```
346
347
### StreamStore (Large Object Storage)
348
349
For storing large objects that don't fit efficiently in regular maps.
350
351
```java { .api }
352
public class StreamStore {
353
public StreamStore(MVStore store);
354
355
// Store large objects
356
public byte[] put(byte[] data);
357
public InputStream put(InputStream in) throws IOException;
358
359
// Retrieve large objects
360
public byte[] get(byte[] id);
361
public InputStream get(byte[] id) throws IOException;
362
363
// Remove large objects
364
public boolean remove(byte[] id);
365
366
// Stream information
367
public long length(byte[] id);
368
}
369
```
370
371
**Usage Examples:**
372
373
```java
374
MVStore store = MVStore.open("blobs.mv");
375
StreamStore streamStore = new StreamStore(store);
376
377
// Store large file
378
File largeFile = new File("large_document.pdf");
379
try (FileInputStream fis = new FileInputStream(largeFile)) {
380
InputStream result = streamStore.put(fis);
381
byte[] id = IOUtils.toByteArray(result); // Get identifier
382
383
// Later retrieve the file
384
try (InputStream retrieved = streamStore.get(id);
385
FileOutputStream fos = new FileOutputStream("retrieved_document.pdf")) {
386
IOUtils.copy(retrieved, fos);
387
}
388
}
389
390
store.close();
391
```
392
393
## Performance and Tuning
394
395
### Cache Configuration
396
397
```java
398
MVStore store = new MVStore.Builder()
399
.fileName("performance.mv")
400
.cacheSize(128) // 128 MB cache
401
.cacheConcurrency(16) // 16-way concurrent cache
402
.open();
403
404
// Runtime cache adjustments
405
store.setCacheSize(256); // Increase to 256 MB
406
```
407
408
### Auto-Commit Settings
409
410
```java
411
MVStore store = new MVStore.Builder()
412
.fileName("autocommit.mv")
413
.autoCommitBufferSize(2048) // 2 MB buffer before auto-commit
414
.open();
415
416
// Disable auto-commit for batch operations
417
MVStore batchStore = new MVStore.Builder()
418
.fileName("batch.mv")
419
.autoCommitDisabled()
420
.open();
421
422
// Manual commit control
423
MVMap<String, String> map = batchStore.openMap("data");
424
for (int i = 0; i < 100000; i++) {
425
map.put("key" + i, "value" + i);
426
if (i % 10000 == 0) {
427
batchStore.commit(); // Periodic commits
428
}
429
}
430
batchStore.commit();
431
```
432
433
### Compression
434
435
```java
436
// Enable compression for space efficiency
437
MVStore compressedStore = new MVStore.Builder()
438
.fileName("compressed.mv")
439
.compress() // Default compression
440
.open();
441
442
// High compression (slower but smaller files)
443
MVStore highCompressStore = new MVStore.Builder()
444
.fileName("high_compress.mv")
445
.compressHigh()
446
.open();
447
```
448
449
## Error Handling and Recovery
450
451
### Exception Handling
452
453
```java { .api }
454
public class MVStoreException extends RuntimeException {
455
public int getErrorCode();
456
public static final int ERROR_WRITING_FAILED = 1;
457
public static final int ERROR_FILE_CORRUPT = 2;
458
public static final int ERROR_FILE_LOCKED = 3;
459
public static final int ERROR_READING_FAILED = 4;
460
// ... other error codes
461
}
462
```
463
464
**Usage Examples:**
465
466
```java
467
try {
468
MVStore store = MVStore.open("problematic.mv");
469
MVMap<String, String> map = store.openMap("data");
470
471
// Operations that might fail
472
map.put("key", "value");
473
store.commit();
474
475
} catch (MVStoreException e) {
476
switch (e.getErrorCode()) {
477
case MVStoreException.ERROR_FILE_LOCKED:
478
System.err.println("Database file is locked by another process");
479
break;
480
case MVStoreException.ERROR_FILE_CORRUPT:
481
System.err.println("Database file is corrupted");
482
break;
483
case MVStoreException.ERROR_WRITING_FAILED:
484
System.err.println("Failed to write to database file");
485
break;
486
default:
487
System.err.println("MVStore error: " + e.getMessage());
488
}
489
}
490
```
491
492
### Panic Mode and Recovery
493
494
```java
495
MVStore store = MVStore.open("recovery.mv");
496
497
// Check for panic state
498
MVStoreException panicException = store.getPanicException();
499
if (panicException != null) {
500
System.err.println("Store is in panic mode: " + panicException.getMessage());
501
// Handle recovery or close immediately
502
store.closeImmediately();
503
return;
504
}
505
506
// Normal operations
507
MVMap<String, String> map = store.openMap("data");
508
// ... use map
509
510
store.close();
511
```
512
513
## Best Practices
514
515
### Resource Management
516
517
```java
518
public class MVStoreManager implements AutoCloseable {
519
private final MVStore store;
520
private final Map<String, MVMap<?, ?>> openMaps;
521
522
public MVStoreManager(String fileName) {
523
this.store = MVStore.open(fileName);
524
this.openMaps = new ConcurrentHashMap<>();
525
}
526
527
@SuppressWarnings("unchecked")
528
public <K, V> MVMap<K, V> getMap(String name, DataType keyType, DataType valueType) {
529
return (MVMap<K, V>) openMaps.computeIfAbsent(name,
530
n -> store.openMap(n, keyType, valueType));
531
}
532
533
public void commit() {
534
store.commit();
535
}
536
537
@Override
538
public void close() {
539
try {
540
store.close();
541
} catch (Exception e) {
542
System.err.println("Error closing MVStore: " + e.getMessage());
543
}
544
}
545
}
546
547
// Usage with try-with-resources
548
try (MVStoreManager manager = new MVStoreManager("app.mv")) {
549
MVMap<String, String> config = manager.getMap("config",
550
new StringDataType(), new StringDataType());
551
552
config.put("app.version", "1.0.0");
553
config.put("app.name", "MyApplication");
554
555
manager.commit();
556
} // Automatically closes store
557
```
558
559
### Concurrent Access
560
561
```java
562
public class ConcurrentMVStore {
563
private final MVStore store;
564
private final MVMap<String, String> data;
565
private final ReadWriteLock lock = new ReentrantReadWriteLock();
566
567
public ConcurrentMVStore(String fileName) {
568
this.store = MVStore.open(fileName);
569
this.data = store.openMap("data");
570
}
571
572
public String get(String key) {
573
lock.readLock().lock();
574
try {
575
return data.get(key);
576
} finally {
577
lock.readLock().unlock();
578
}
579
}
580
581
public void put(String key, String value) {
582
lock.writeLock().lock();
583
try {
584
data.put(key, value);
585
store.commit();
586
} finally {
587
lock.writeLock().unlock();
588
}
589
}
590
591
public void close() {
592
lock.writeLock().lock();
593
try {
594
store.close();
595
} finally {
596
lock.writeLock().unlock();
597
}
598
}
599
}
600
```