提交 570437e8 authored 作者: Thomas Mueller's avatar Thomas Mueller

Improved performance for read operations.

上级 44027adc
......@@ -12,13 +12,15 @@ import java.util.Iterator;
* A cursor to iterate over elements in ascending order.
*
* @param <K> the key type
* @param <V> the value type
*/
public class Cursor<K> implements Iterator<K> {
public class Cursor<K, V> implements Iterator<K> {
private final MVMap<K, ?> map;
private final K from;
private CursorPos pos;
private K current;
private V currentValue, lastValue;
private final Page root;
private boolean initialized;
......@@ -42,9 +44,19 @@ public class Cursor<K> implements Iterator<K> {
public K next() {
hasNext();
K c = current;
lastValue = currentValue;
fetchNext();
return c;
}
/**
* Get the last read value if there was one.
*
* @return the value or null
*/
public V getValue() {
return lastValue;
}
/**
* Skip over that many entries. This method is relatively fast (for this map
......@@ -110,7 +122,9 @@ public class Cursor<K> implements Iterator<K> {
private void fetchNext() {
while (pos != null) {
if (pos.index < pos.page.getKeyCount()) {
current = (K) pos.page.getKey(pos.index++);
int index = pos.index++;
current = (K) pos.page.getKey(index);
currentValue = (V) pos.page.getValue(index);
return;
}
pos = pos.parent;
......
......@@ -17,6 +17,8 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Map;
import org.h2.engine.Constants;
import org.h2.util.New;
......@@ -852,5 +854,39 @@ public class DataUtils {
return errorValue;
}
}
/**
* An entry of a map.
*
* @param <K> the key type
* @param <V> the value type
*/
public static class MapEntry<K, V> implements Map.Entry<K, V> {
private final K key;
private V value;
public MapEntry(K key, V value) {
this.key = key;
this.value = value;
}
@Override
public K getKey() {
return key;
}
@Override
public V getValue() {
return value;
}
@Override
public V setValue(V value) {
throw DataUtils.newUnsupportedOperationException(
"Updating the value is not supported");
}
}
}
......@@ -745,20 +745,30 @@ public class MVMap<K, V> extends AbstractMap<K, V>
}
/**
* Iterate over all keys.
* Iterate over a number of keys.
*
* @param from the first key to return
* @return the iterator
*/
public Cursor<K> keyIterator(K from) {
return new Cursor<K>(this, root, from);
public Iterator<K> keyIterator(K from) {
return new Cursor<K, V>(this, root, from);
}
/**
* Get a cursor to iterate over a number of keys and values.
*
* @param from the first key to return
* @return the cursor
*/
public Cursor<K, V> cursor(K from) {
return new Cursor<K, V>(this, root, from);
}
@Override
public Set<Map.Entry<K, V>> entrySet() {
HashMap<K, V> map = new HashMap<K, V>();
for (K k : keySet()) {
map.put(k, get(k));
for (Cursor<K, V> cursor = cursor(null); cursor.hasNext();) {
map.put(cursor.next(), cursor.getValue());
}
return map.entrySet();
}
......@@ -771,7 +781,7 @@ public class MVMap<K, V> extends AbstractMap<K, V>
@Override
public Iterator<K> iterator() {
return new Cursor<K>(map, root, null);
return new Cursor<K, V>(map, root, null);
}
@Override
......
......@@ -17,6 +17,7 @@ import org.h2.result.SortOrder;
import org.h2.table.Column;
import org.h2.table.IndexColumn;
import org.h2.table.TableFilter;
import org.h2.value.ValueLong;
/**
* An index that delegates indexing to another index.
......@@ -53,10 +54,10 @@ public class MVDelegateIndex extends BaseIndex {
@Override
public Cursor find(Session session, SearchRow first, SearchRow last) {
long min = mainIndex.getKey(first, Long.MIN_VALUE, Long.MIN_VALUE);
ValueLong min = mainIndex.getKey(first, MVPrimaryIndex.MIN, MVPrimaryIndex.MIN);
// ifNull is MIN_VALUE as well, because the column is never NULL
// so avoid returning all rows (returning one row is OK)
long max = mainIndex.getKey(last, Long.MAX_VALUE, Long.MIN_VALUE);
ValueLong max = mainIndex.getKey(last, MVPrimaryIndex.MAX, MVPrimaryIndex.MIN);
return mainIndex.find(session, min, max);
}
......
......@@ -9,6 +9,8 @@ package org.h2.mvstore.db;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import org.h2.constant.ErrorCode;
import org.h2.engine.Constants;
......@@ -18,6 +20,7 @@ import org.h2.index.BaseIndex;
import org.h2.index.Cursor;
import org.h2.index.IndexType;
import org.h2.message.DbException;
import org.h2.mvstore.DataUtils;
import org.h2.mvstore.db.TransactionStore.Transaction;
import org.h2.mvstore.db.TransactionStore.TransactionMap;
import org.h2.result.Row;
......@@ -36,6 +39,10 @@ import org.h2.value.ValueNull;
*/
public class MVPrimaryIndex extends BaseIndex {
static final ValueLong MIN = ValueLong.get(Long.MIN_VALUE);
static final ValueLong MAX = ValueLong.get(Long.MAX_VALUE);
static final ValueLong ZERO = ValueLong.get(0);
private final MVTable mvTable;
private final String mapName;
private TransactionMap<Value, Value> dataMap;
......@@ -152,30 +159,30 @@ public class MVPrimaryIndex extends BaseIndex {
@Override
public Cursor find(Session session, SearchRow first, SearchRow last) {
long min, max;
ValueLong min, max;
if (first == null || mainIndexColumn < 0) {
min = Long.MIN_VALUE;
min = MIN;
} else {
Value v = first.getValue(mainIndexColumn);
ValueLong v = (ValueLong) first.getValue(mainIndexColumn);
if (v == null) {
min = 0;
min = ZERO;
} else {
min = v.getLong();
min = v;
}
}
if (last == null || mainIndexColumn < 0) {
max = Long.MAX_VALUE;
max = MAX;
} else {
Value v = last.getValue(mainIndexColumn);
ValueLong v = (ValueLong) last.getValue(mainIndexColumn);
if (v == null) {
max = Long.MAX_VALUE;
max = MAX;
} else {
max = v.getLong();
max = v;
}
}
TransactionMap<Value, Value> map = getMap(session);
return new MVStoreCursor(session, map.keyIterator(
ValueLong.get(min)), max);
return new MVStoreCursor(map.entryIterator(
min), max);
}
@Override
......@@ -196,7 +203,7 @@ public class MVPrimaryIndex extends BaseIndex {
@Override
public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
try {
long cost = 10 * (dataMap.map.sizeAsLong() + Constants.COST_ROW_OFFSET);
long cost = 10 * (dataMap.sizeAsLongEstimated() + Constants.COST_ROW_OFFSET);
return cost;
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
......@@ -236,15 +243,15 @@ public class MVPrimaryIndex extends BaseIndex {
@Override
public Cursor findFirstOrLast(Session session, boolean first) {
TransactionMap<Value, Value> map = getMap(session);
Value v = first ? map.firstKey() : map.lastKey();
ValueLong v = (ValueLong) (first ? map.firstKey() : map.lastKey());
if (v == null) {
return new MVStoreCursor(session, Collections.<Value>emptyList().iterator(), 0);
return new MVStoreCursor(Collections.<Entry<Value, Value>>emptyList().iterator(), null);
}
long key = v.getLong();
MVStoreCursor cursor = new MVStoreCursor(session,
Arrays.asList((Value) ValueLong.get(key)).iterator(), key);
cursor.next();
return cursor;
Value value = map.get(v);
Entry<Value, Value> e = new DataUtils.MapEntry<Value, Value>(v, value);
@SuppressWarnings("unchecked")
List<Entry<Value, Value>> list = Arrays.asList(e);
return new MVStoreCursor(list.iterator(), v);
}
@Override
......@@ -261,7 +268,7 @@ public class MVPrimaryIndex extends BaseIndex {
@Override
public long getRowCountApproximation() {
try {
return dataMap.map.sizeAsLong();
return dataMap.sizeAsLongEstimated();
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
}
......@@ -290,7 +297,7 @@ public class MVPrimaryIndex extends BaseIndex {
* @param ifNull the value to use if the column is NULL
* @return the key
*/
long getKey(SearchRow row, long ifEmpty, long ifNull) {
ValueLong getKey(SearchRow row, ValueLong ifEmpty, ValueLong ifNull) {
if (row == null) {
return ifEmpty;
}
......@@ -300,7 +307,7 @@ public class MVPrimaryIndex extends BaseIndex {
} else if (v == ValueNull.INSTANCE) {
return ifNull;
}
return v.getLong();
return (ValueLong) v.convertTo(Value.LONG);
}
/**
......@@ -311,9 +318,9 @@ public class MVPrimaryIndex extends BaseIndex {
* @param last the key of the last row
* @return the cursor
*/
Cursor find(Session session, long first, long last) {
Cursor find(Session session, ValueLong first, ValueLong last) {
TransactionMap<Value, Value> map = getMap(session);
return new MVStoreCursor(session, map.keyIterator(ValueLong.get(first)), last);
return new MVStoreCursor(map.entryIterator(first), last);
}
@Override
......@@ -340,14 +347,12 @@ public class MVPrimaryIndex extends BaseIndex {
*/
class MVStoreCursor implements Cursor {
private final Session session;
private final Iterator<Value> it;
private final long last;
private ValueLong current;
private final Iterator<Entry<Value, Value>> it;
private final ValueLong last;
private Entry<Value, Value> current;
private Row row;
public MVStoreCursor(Session session, Iterator<Value> it, long last) {
this.session = session;
public MVStoreCursor(Iterator<Entry<Value, Value>> it, ValueLong last) {
this.it = it;
this.last = last;
}
......@@ -356,7 +361,9 @@ public class MVPrimaryIndex extends BaseIndex {
public Row get() {
if (row == null) {
if (current != null) {
row = getRow(session, current.getLong());
ValueArray array = (ValueArray) current.getValue();
row = new Row(array.getList(), 0);
row.setKey(current.getKey().getLong());
}
}
return row;
......@@ -369,8 +376,8 @@ public class MVPrimaryIndex extends BaseIndex {
@Override
public boolean next() {
current = (ValueLong) it.next();
if (current != null && current.getLong() > last) {
current = it.next();
if (current != null && current.getKey().getLong() > last.getLong()) {
current = null;
}
row = null;
......
......@@ -66,7 +66,7 @@ public class MVSecondaryIndex extends BaseIndex {
ValueDataType valueType = new ValueDataType(null, null, null);
dataMap = mvTable.getTransaction(null).openMap(
mapName, keyType, valueType);
if (keyType != dataMap.map.getKeyType()) {
if (keyType != dataMap.getKeyType()) {
throw DbException.throwInternalError("Incompatible key type");
}
}
......@@ -102,6 +102,7 @@ public class MVSecondaryIndex extends BaseIndex {
}
if (indexType.isUnique()) {
// check if there is another (uncommitted) entry
// TODO use entry iterator
Iterator<Value> it = map.keyIterator(unique, true);
while (it.hasNext()) {
ValueArray k = (ValueArray) it.next();
......@@ -195,7 +196,7 @@ public class MVSecondaryIndex extends BaseIndex {
@Override
public double getCost(Session session, int[] masks, TableFilter filter, SortOrder sortOrder) {
try {
return 10 * getCostRangeIndex(masks, dataMap.map.sizeAsLong(), filter, sortOrder);
return 10 * getCostRangeIndex(masks, dataMap.sizeAsLongEstimated(), filter, sortOrder);
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
}
......@@ -244,7 +245,7 @@ public class MVSecondaryIndex extends BaseIndex {
@Override
public boolean needRebuild() {
try {
return dataMap.map.sizeAsLong() == 0;
return dataMap.sizeAsLongEstimated() == 0;
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
}
......@@ -259,7 +260,7 @@ public class MVSecondaryIndex extends BaseIndex {
@Override
public long getRowCountApproximation() {
try {
return dataMap.map.sizeAsLong();
return dataMap.sizeAsLongEstimated();
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
}
......
......@@ -282,7 +282,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex {
@Override
public boolean needRebuild() {
try {
return dataMap.map.sizeAsLong() == 0;
return dataMap.sizeAsLongEstimated() == 0;
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
}
......@@ -297,7 +297,7 @@ public class MVSpatialIndex extends BaseIndex implements SpatialIndex {
@Override
public long getRowCountApproximation() {
try {
return dataMap.map.sizeAsLong();
return dataMap.sizeAsLongEstimated();
} catch (IllegalStateException e) {
throw DbException.get(ErrorCode.OBJECT_CLOSED);
}
......
......@@ -12,10 +12,12 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.h2.mvstore.Cursor;
import org.h2.mvstore.DataUtils;
import org.h2.mvstore.MVMap;
import org.h2.mvstore.MVMapConcurrent;
import org.h2.mvstore.MVStore;
import org.h2.mvstore.WriteBuffer;
import org.h2.mvstore.type.DataType;
......@@ -26,6 +28,11 @@ import org.h2.util.New;
* A store that supports concurrent transactions.
*/
public class TransactionStore {
/**
* Whether the concurrent maps should be used.
*/
private static final boolean CONCURRENT = false;
/**
* The store.
......@@ -241,7 +248,7 @@ public class TransactionStore {
}
}
<K, V> void removeMap(TransactionMap<K, V> map) {
synchronized <K, V> void removeMap(TransactionMap<K, V> map) {
maps.remove(map.mapId);
store.removeMap(map.map);
}
......@@ -256,6 +263,7 @@ public class TransactionStore {
if (store.isClosed()) {
return;
}
// TODO could synchronize on blocks
synchronized (undoLog) {
t.setStatus(Transaction.STATUS_COMMITTING);
for (long logId = 0; logId < maxLogId; logId++) {
......@@ -293,6 +301,32 @@ public class TransactionStore {
}
endTransaction(t);
}
synchronized <K> MVMap<K, VersionedValue> openMap(String name, DataType keyType, DataType valueType) {
if (keyType == null) {
keyType = new ObjectDataType();
}
if (valueType == null) {
valueType = new ObjectDataType();
}
VersionedValueType vt = new VersionedValueType(valueType);
MVMap<K, VersionedValue> map;
if (CONCURRENT) {
MVMapConcurrent.Builder<K, VersionedValue> builder =
new MVMapConcurrent.Builder<K, VersionedValue>().
keyType(keyType).valueType(vt);
map = store.openMap(name, builder);
} else {
MVMap.Builder<K, VersionedValue> builder =
new MVMap.Builder<K, VersionedValue>().
keyType(keyType).valueType(vt);
map = store.openMap(name, builder);
}
@SuppressWarnings("unchecked")
MVMap<Object, VersionedValue> m = (MVMap<Object, VersionedValue>) map;
maps.put(map.getId(), m);
return map;
}
synchronized MVMap<Object, VersionedValue> openMap(int mapId) {
MVMap<Object, VersionedValue> map = maps.get(mapId);
......@@ -316,17 +350,6 @@ public class TransactionStore {
return map;
}
/**
* Check whether the given transaction id is still open and contains log
* entries.
*
* @param transactionId the transaction id
* @return true if it is open
*/
boolean isTransactionOpen(int transactionId) {
return transactionId != 0;
}
/**
* End this transaction
*
......@@ -362,6 +385,7 @@ public class TransactionStore {
* @param toLogId the log id to roll back to
*/
void rollbackTo(Transaction t, long maxLogId, long toLogId) {
// TODO could synchronize on blocks
synchronized (undoLog) {
for (long logId = maxLogId - 1; logId >= toLogId; logId--) {
Long undoKey = getOperationId(t.getId(), logId);
......@@ -619,16 +643,7 @@ public class TransactionStore {
*/
public <K, V> TransactionMap<K, V> openMap(String name, DataType keyType, DataType valueType) {
checkNotClosed();
if (keyType == null) {
keyType = new ObjectDataType();
}
if (valueType == null) {
valueType = new ObjectDataType();
}
VersionedValueType vt = new VersionedValueType(valueType);
MVMap.Builder<K, VersionedValue> builder = new MVMap.Builder<K, VersionedValue>()
.keyType(keyType).valueType(vt);
MVMap<K, VersionedValue> map = store.store.openMap(name, builder);
MVMap<K, VersionedValue> map = store.openMap(name, keyType, valueType);
int mapId = map.getId();
return new TransactionMap<K, V>(this, map, mapId);
}
......@@ -733,28 +748,28 @@ public class TransactionStore {
*/
public static class TransactionMap<K, V> {
/**
* The map used for writing (the latest version).
* <p>
* Key: key the key of the data.
* Value: { transactionId, oldVersion, value }
*/
final MVMap<K, VersionedValue> map;
/**
* The map id.
*/
final int mapId;
private Transaction transaction;
/**
* If a record was read that was updated by this transaction, and the
* update occurred before this log id, the older version is read. This
* is so that changes are not immediately visible, to support statement
* processing (for example "update test set id = id + 1").
*/
private long readLogId = Long.MAX_VALUE;
long readLogId = Long.MAX_VALUE;
/**
* The map used for writing (the latest version).
* <p>
* Key: key the key of the data.
* Value: { transactionId, oldVersion, value }
*/
final MVMap<K, VersionedValue> map;
private Transaction transaction;
TransactionMap(Transaction transaction, MVMap<K, VersionedValue> map, int mapId) {
this.transaction = transaction;
......@@ -784,6 +799,15 @@ public class TransactionStore {
m.setSavepoint(savepoint);
return m;
}
/**
* Get the size of the raw map.
*
* @return the size
*/
public long sizeAsLongEstimated() {
return map.sizeAsLong();
}
/**
* Get the size of the map as seen by this transaction.
......@@ -793,10 +817,11 @@ public class TransactionStore {
public long sizeAsLong() {
// TODO this method is very slow
long size = 0;
Cursor<K> cursor = map.keyIterator(null);
Cursor<K, VersionedValue> cursor = map.cursor(null);
while (cursor.hasNext()) {
K key = cursor.next();
if (get(key) != null) {
VersionedValue data = cursor.getValue();
if (getValue(key, readLogId, data) != null) {
size++;
}
}
......@@ -942,26 +967,26 @@ public class TransactionStore {
}
return true;
}
int tx = getTransactionId(current.operationId);
if (tx == transaction.transactionId) {
// added or updated by this transaction
long id = current.operationId;
if (id == 0) {
// committed
transaction.log(mapId, key, current);
// the transaction is committed:
// overwrite the value
if (!map.replace(key, current, newValue)) {
// strange, somebody overwrite the value
// even thought the change was not committed
// somebody else was faster
transaction.logUndo();
return false;
}
return true;
}
// added or updated by another transaction
boolean open = transaction.store.isTransactionOpen(tx);
if (!open) {
int tx = getTransactionId(current.operationId);
if (tx == transaction.transactionId) {
// added or updated by this transaction
transaction.log(mapId, key, current);
// the transaction is committed:
// overwrite the value
if (!map.replace(key, current, newValue)) {
// somebody else was faster
// strange, somebody overwrite the value
// even thought the change was not committed
transaction.logUndo();
return false;
}
......@@ -1033,6 +1058,10 @@ public class TransactionStore {
private VersionedValue getValue(K key, long maxLog) {
VersionedValue data = map.get(key);
return getValue(key, maxLog, data);
}
VersionedValue getValue(K key, long maxLog, VersionedValue data) {
for (int i = 0; i < 10; i++) {
int tx;
if (data == null) {
......@@ -1040,25 +1069,21 @@ public class TransactionStore {
return null;
}
long id = data.operationId;
if (id == 0) {
// it is committed
return data;
}
tx = getTransactionId(id);
long logId = getLogId(id);
if (tx == transaction.transactionId) {
// added by this transaction
if (logId < maxLog) {
if (getLogId(id) < maxLog) {
return data;
}
}
// added, updated, or removed by another transaction
boolean open = transaction.store.isTransactionOpen(tx);
if (!open) {
// it is committed
return data;
}
// get the value before the uncommitted transaction
Long x = getOperationId(tx, logId);
Object[] d;
synchronized (transaction.store.undoLog) {
d = transaction.store.undoLog.get(x);
d = transaction.store.undoLog.get(id);
}
if (d == null) {
// this entry was committed or rolled back
......@@ -1125,7 +1150,7 @@ public class TransactionStore {
* @return the result
*/
public K getLatestCeilingKey(K key) {
Cursor<K> cursor = map.keyIterator(key);
Iterator<K> cursor = map.keyIterator(key);
while (cursor.hasNext()) {
key = cursor.next();
if (get(key, Long.MAX_VALUE) != null) {
......@@ -1143,7 +1168,7 @@ public class TransactionStore {
*/
public K ceilingKey(K key) {
// TODO this method is slow
Cursor<K> cursor = map.keyIterator(key);
Iterator<K> cursor = map.keyIterator(key);
while (cursor.hasNext()) {
key = cursor.next();
if (get(key) != null) {
......@@ -1195,9 +1220,104 @@ public class TransactionStore {
* @return the iterator
*/
public Iterator<K> keyIterator(K from, boolean includeUncommitted) {
Cursor<K> it = map.keyIterator(from);
Iterator<K> it = map.keyIterator(from);
return wrapIterator(it, includeUncommitted);
}
public Iterator<Entry<K, V>> entryIterator(K from) {
final Cursor<K, VersionedValue> cursor = map.cursor(from);
return new Iterator<Entry<K, V>>() {
private Entry<K, V> current;
{
fetchNext();
}
private void fetchNext() {
while (cursor.hasNext()) {
final K key = cursor.next();
VersionedValue data = cursor.getValue();
data = getValue(key, readLogId, data);
if (data != null && data.value != null) {
@SuppressWarnings("unchecked")
final V value = (V) data.value;
current = new DataUtils.MapEntry<K, V>(key, value);
return;
}
}
current = null;
}
@Override
public boolean hasNext() {
return current != null;
}
@Override
public Entry<K, V> next() {
Entry<K, V> result = current;
fetchNext();
return result;
}
@Override
public void remove() {
throw DataUtils.newUnsupportedOperationException(
"Removing is not supported");
}
};
}
/**
* Iterate over keys.
*
* @param cursor the cursor to wrap
* @param includeUncommitted whether uncommitted entries should be included
* @return the iterator
*/
public Iterator<K> wrapCursor(final Cursor<K, VersionedValue> cursor, final boolean includeUncommitted) {
return new Iterator<K>() {
private K current;
{
fetchNext();
}
private void fetchNext() {
while (cursor.hasNext()) {
current = cursor.next();
if (includeUncommitted) {
return;
}
VersionedValue data = cursor.getValue();
data = getValue(current, readLogId, data);
if (data != null && data.value != null) {
return;
}
}
current = null;
}
@Override
public boolean hasNext() {
return current != null;
}
@Override
public K next() {
K result = current;
fetchNext();
return result;
}
@Override
public void remove() {
throw DataUtils.newUnsupportedOperationException(
"Removing is not supported");
}
};
}
/**
* Iterate over keys.
......@@ -1207,6 +1327,7 @@ public class TransactionStore {
* @return the iterator
*/
public Iterator<K> wrapIterator(final Iterator<K> iterator, final boolean includeUncommitted) {
// TODO duplicate code for wrapCursor and wrapIterator
return new Iterator<K>() {
private K current;
......@@ -1251,6 +1372,10 @@ public class TransactionStore {
return transaction;
}
public DataType getKeyType() {
return map.getKeyType();
}
}
/**
......
/*
* Copyright 2004-2013 H2 Group. Multiple-Licensed under the H2 License, Version
* 1.0, and under the Eclipse Public License, Version 1.0
* (http://h2database.com/html/license.html). Initial Developer: H2 Group
*/
package org.h2.test.store;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.Statement;
import org.h2.mvstore.Chunk;
import org.h2.mvstore.MVStore;
import org.h2.mvstore.Page;
import org.h2.mvstore.db.TransactionStore;
import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase;
import org.h2.util.Profiler;
import org.h2.value.ValueLong;
import org.h2.value.ValueString;
/**
* Tests performance and helps analyze bottlenecks.
*/
public class TestBenchmark extends TestBase {
/**
* Run just this test.
*
* @param a ignored
*/
public static void main(String... a) throws Exception {
TestBase.createCaller().init().test();
}
@Override
public void test() throws Exception {
test(true);
test(false);
test(true);
test(false);
test(true);
test(false);
}
private void test(boolean mvStore) throws Exception {
FileUtils.deleteRecursive(getBaseDir(), true);
Connection conn;
Statement stat;
String url = "mvstore";
if (mvStore) {
url += ";MV_STORE=TRUE;LOG=0";
}
// 2033 mvstore
// 2313 (2075?) default
url = getURL(url, true);
conn = getConnection(url);
stat = conn.createStatement();
stat.execute("create table test(id bigint primary key, name varchar)");
conn.setAutoCommit(false);
PreparedStatement prep = conn.prepareStatement("insert into test values(?, ?)");
String data = "Hello World";
int rowCount = 100000;
int readCount = 20* rowCount;
for (int i = 0; i < rowCount; i++) {
prep.setInt(1, i);
prep.setString(2, data);
prep.execute();
if (i % 100 == 0) {
conn.commit();
}
}
long start = System.currentTimeMillis();
// Profiler prof = new Profiler();
// prof.sumClasses=true;
// prof.startCollecting();
;
prep = conn.prepareStatement("select * from test where id = ?");
for (int i = 0; i < readCount; i++) {
prep.setInt(1, i % rowCount);
prep.executeQuery();
}
//System.out.println("Transactionstore.counter " + ValueLong.counter);
//System.out.println("count " + Page.writeCount + " avgLen " + (1.0*Page.writeLength/Page.writeCount) + " avgSize " + (1.0*Page.writeSize/Page.writeCount));
//System.out.println(prof.getTop(5));
//System.out.println("ountUnc:" + counterUnc);
//System.out.println("ount:" + counter);
System.out.println((System.currentTimeMillis() - start) + " " + (mvStore ? "mvstore" : "default"));
conn.close();
// MVStore s = new MVStore.Builder().fileName(getBaseDir() + "/mvstore.mv.db").open();
// int count = 0;
// long length = 0;
// for(String k : s.getMetaMap().keyList()) {
// if (k.startsWith("chunk.")) {
// String x = s.getMetaMap().get(k);
// Chunk c = Chunk.fromString(x);
// if (c.length < Integer.MAX_VALUE) {
// count++;
// length += c.length;
// }
// }
// }
// if (count > 0) {
// System.out.println("chunks: " + count + " average length: " + (length / count));
// }
// s.close();
}
}
......@@ -706,7 +706,7 @@ public class TestMVStore extends TestBase {
map.put(i, 10 * i);
}
Cursor<Integer> c = map.keyIterator(50);
Cursor<Integer, Integer> c = map.cursor(50);
// skip must reset the root of the cursor
c.skip(10);
for (int i = 70; i < 100; i += 2) {
......@@ -732,7 +732,7 @@ public class TestMVStore extends TestBase {
}
}
// skip
c = map.keyIterator(0);
c = map.cursor(0);
assertTrue(c.hasNext());
assertEquals(0, c.next().intValue());
c.skip(0);
......@@ -742,11 +742,11 @@ public class TestMVStore extends TestBase {
c.skip(20);
assertEquals(48, c.next().intValue());
c = map.keyIterator(0);
c = map.cursor(0);
c.skip(20);
assertEquals(40, c.next().intValue());
c = map.keyIterator(0);
c = map.cursor(0);
assertEquals(0, c.next().intValue());
assertEquals(12, map.keyList().indexOf(24));
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论