提交 237b1a55 authored 作者: Thomas Mueller's avatar Thomas Mueller

MVStore table engine - now almost as fast as the page store (WIP)

上级 c9e973a3
...@@ -23,8 +23,8 @@ import org.h2.jdbc.JdbcConnection; ...@@ -23,8 +23,8 @@ import org.h2.jdbc.JdbcConnection;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.message.TraceSystem; import org.h2.message.TraceSystem;
import org.h2.mvstore.db.TransactionStore; import org.h2.mvstore.db.TransactionStore2;
import org.h2.mvstore.db.TransactionStore.Transaction; import org.h2.mvstore.db.TransactionStore2.Transaction;
import org.h2.result.ResultInterface; import org.h2.result.ResultInterface;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.schema.Schema; import org.h2.schema.Schema;
...@@ -106,7 +106,6 @@ public class Session extends SessionWithState { ...@@ -106,7 +106,6 @@ public class Session extends SessionWithState {
private SmallLRUCache<String, Command> queryCache; private SmallLRUCache<String, Command> queryCache;
private Transaction transaction; private Transaction transaction;
private long startStatement = -1; private long startStatement = -1;
private long statementVersion;
public Session(Database database, User user, int id) { public Session(Database database, User user, int id) {
this.database = database; this.database = database;
...@@ -1258,20 +1257,19 @@ public class Session extends SessionWithState { ...@@ -1258,20 +1257,19 @@ public class Session extends SessionWithState {
* @param store the store * @param store the store
* @return the transaction * @return the transaction
*/ */
public Transaction getTransaction(TransactionStore store) { public Transaction getTransaction(TransactionStore2 store) {
if (transaction == null) { if (transaction == null) {
transaction = store.begin(); transaction = store.begin();
statementVersion = -1; startStatement = -1;
} }
return transaction; return transaction;
} }
public long getStatementVersion() { public long getStatementSavepoint() {
if (startStatement == -1) { if (startStatement == -1) {
startStatement = transaction.setSavepoint(); startStatement = transaction.setSavepoint();
statementVersion = transaction.getCurrentVersion();
} }
return statementVersion; return startStatement;
} }
} }
...@@ -35,7 +35,9 @@ public class MVDelegateIndex extends BaseIndex { ...@@ -35,7 +35,9 @@ public class MVDelegateIndex extends BaseIndex {
} }
} }
static int count;
public void add(Session session, Row row) { public void add(Session session, Row row) {
count++;
// nothing to do // nothing to do
} }
......
...@@ -18,8 +18,8 @@ import org.h2.index.Cursor; ...@@ -18,8 +18,8 @@ import org.h2.index.Cursor;
import org.h2.index.IndexType; import org.h2.index.IndexType;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.MVMap; import org.h2.mvstore.MVMap;
import org.h2.mvstore.db.TransactionStore.Transaction; import org.h2.mvstore.db.TransactionStore2.Transaction;
import org.h2.mvstore.db.TransactionStore.TransactionMap; import org.h2.mvstore.db.TransactionStore2.TransactionMap;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.result.SortOrder; import org.h2.result.SortOrder;
...@@ -37,7 +37,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -37,7 +37,7 @@ public class MVPrimaryIndex extends BaseIndex {
private final MVTable mvTable; private final MVTable mvTable;
private String mapName; private String mapName;
private MVMap.Builder<Value, Value> mapBuilder; private TransactionMap<Value, Value> dataMap;
private long lastKey; private long lastKey;
private int mainIndexColumn = -1; private int mainIndexColumn = -1;
...@@ -54,12 +54,12 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -54,12 +54,12 @@ public class MVPrimaryIndex extends BaseIndex {
ValueDataType valueType = new ValueDataType( ValueDataType valueType = new ValueDataType(
db.getCompareMode(), db, sortTypes); db.getCompareMode(), db, sortTypes);
mapName = getName() + "_" + getId(); mapName = getName() + "_" + getId();
mapBuilder = new MVMap.Builder<Value, Value>(). MVMap.Builder<Value, Value> mapBuilder = new MVMap.Builder<Value, Value>().
keyType(keyType). keyType(keyType).
valueType(valueType); valueType(valueType);
TransactionMap<Value, Value> map = getMap(null); dataMap = mvTable.getTransaction(null).openMap(mapName, mapBuilder);
Value k = map.lastKey(); Value k = dataMap.lastKey();
map.getTransaction().commit(); dataMap.getTransaction().commit();
lastKey = k == null ? 0 : k.getLong(); lastKey = k == null ? 0 : k.getLong();
} }
...@@ -108,10 +108,6 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -108,10 +108,6 @@ public class MVPrimaryIndex extends BaseIndex {
Long c = row.getValue(mainIndexColumn).getLong(); Long c = row.getValue(mainIndexColumn).getLong();
row.setKey(c); row.setKey(c);
} }
Value[] array = new Value[columns.length];
for (int i = 0; i < array.length; i++) {
array[i] = row.getValue(i);
}
TransactionMap<Value, Value> map = getMap(session); TransactionMap<Value, Value> map = getMap(session);
if (map.containsKey(ValueLong.get(row.getKey()))) { if (map.containsKey(ValueLong.get(row.getKey()))) {
String sql = "PRIMARY KEY ON " + table.getSQL(); String sql = "PRIMARY KEY ON " + table.getSQL();
...@@ -122,7 +118,7 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -122,7 +118,7 @@ public class MVPrimaryIndex extends BaseIndex {
e.setSource(this); e.setSource(this);
throw e; throw e;
} }
map.put(ValueLong.get(row.getKey()), ValueArray.get(array)); map.put(ValueLong.get(row.getKey()), ValueArray.get(row.getValueList()));
lastKey = Math.max(lastKey, row.getKey()); lastKey = Math.max(lastKey, row.getKey());
} }
...@@ -287,6 +283,25 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -287,6 +283,25 @@ public class MVPrimaryIndex extends BaseIndex {
return new MVStoreCursor(session, map.keyIterator(ValueLong.get(first)), last); return new MVStoreCursor(session, map.keyIterator(ValueLong.get(first)), last);
} }
public boolean isRowIdIndex() {
return true;
}
/**
* Get the map to store the data.
*
* @param session the session
* @return the map
*/
TransactionMap<Value, Value> getMap(Session session) {
if (session == null) {
return dataMap;
}
Transaction t = mvTable.getTransaction(session);
long savepoint = session.getStatementSavepoint();
return dataMap.getInstance(t, savepoint);
}
/** /**
* A cursor. * A cursor.
*/ */
...@@ -337,23 +352,4 @@ public class MVPrimaryIndex extends BaseIndex { ...@@ -337,23 +352,4 @@ public class MVPrimaryIndex extends BaseIndex {
} }
public boolean isRowIdIndex() {
return true;
}
/**
* Get the map to store the data.
*
* @param session the session
* @return the map
*/
TransactionMap<Value, Value> getMap(Session session) {
if (session == null) {
return mvTable.getTransaction(null).openMap(mapName, -1, mapBuilder);
}
Transaction t = mvTable.getTransaction(session);
long version = session.getStatementVersion();
return t.openMap(mapName, version, mapBuilder);
}
} }
...@@ -17,8 +17,8 @@ import org.h2.index.Cursor; ...@@ -17,8 +17,8 @@ import org.h2.index.Cursor;
import org.h2.index.IndexType; import org.h2.index.IndexType;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.mvstore.MVMap; import org.h2.mvstore.MVMap;
import org.h2.mvstore.db.TransactionStore.Transaction; import org.h2.mvstore.db.TransactionStore2.Transaction;
import org.h2.mvstore.db.TransactionStore.TransactionMap; import org.h2.mvstore.db.TransactionStore2.TransactionMap;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SearchRow; import org.h2.result.SearchRow;
import org.h2.result.SortOrder; import org.h2.result.SortOrder;
...@@ -42,7 +42,7 @@ public class MVSecondaryIndex extends BaseIndex { ...@@ -42,7 +42,7 @@ public class MVSecondaryIndex extends BaseIndex {
private final int keyColumns; private final int keyColumns;
private String mapName; private String mapName;
private MVMap.Builder<Value, Value> mapBuilder; private TransactionMap<Value, Value> dataMap;
public MVSecondaryIndex(Database db, MVTable table, int id, String indexName, public MVSecondaryIndex(Database db, MVTable table, int id, String indexName,
IndexColumn[] columns, IndexType indexType) { IndexColumn[] columns, IndexType indexType) {
...@@ -63,9 +63,10 @@ public class MVSecondaryIndex extends BaseIndex { ...@@ -63,9 +63,10 @@ public class MVSecondaryIndex extends BaseIndex {
ValueDataType keyType = new ValueDataType( ValueDataType keyType = new ValueDataType(
db.getCompareMode(), db, sortTypes); db.getCompareMode(), db, sortTypes);
ValueDataType valueType = new ValueDataType(null, null, null); ValueDataType valueType = new ValueDataType(null, null, null);
mapBuilder = new MVMap.Builder<Value, Value>(). MVMap.Builder<Value, Value> mapBuilder = new MVMap.Builder<Value, Value>().
keyType(keyType). keyType(keyType).
valueType(valueType); valueType(valueType);
dataMap = mvTable.getTransaction(null).openMap(mapName, mapBuilder);
} }
private static void checkIndexColumnTypes(IndexColumn[] columns) { private static void checkIndexColumnTypes(IndexColumn[] columns) {
...@@ -247,6 +248,21 @@ public class MVSecondaryIndex extends BaseIndex { ...@@ -247,6 +248,21 @@ public class MVSecondaryIndex extends BaseIndex {
// ok // ok
} }
/**
* Get the map to store the data.
*
* @param session the session
* @return the map
*/
TransactionMap<Value, Value> getMap(Session session) {
if (session == null) {
return dataMap;
}
Transaction t = mvTable.getTransaction(session);
long savepoint = session.getStatementSavepoint();
return dataMap.getInstance(t, savepoint);
}
/** /**
* A cursor. * A cursor.
*/ */
...@@ -308,19 +324,4 @@ public class MVSecondaryIndex extends BaseIndex { ...@@ -308,19 +324,4 @@ public class MVSecondaryIndex extends BaseIndex {
} }
/**
* Get the map to store the data.
*
* @param session the session
* @return the map
*/
TransactionMap<Value, Value> getMap(Session session) {
if (session == null) {
return mvTable.getTransaction(null).openMap(mapName, -1, mapBuilder);
}
Transaction t = mvTable.getTransaction(session);
long version = session.getStatementVersion();
return t.openMap(mapName, version, mapBuilder);
}
} }
...@@ -28,7 +28,7 @@ import org.h2.index.IndexType; ...@@ -28,7 +28,7 @@ import org.h2.index.IndexType;
import org.h2.index.MultiVersionIndex; import org.h2.index.MultiVersionIndex;
import org.h2.message.DbException; import org.h2.message.DbException;
import org.h2.message.Trace; import org.h2.message.Trace;
import org.h2.mvstore.db.TransactionStore.Transaction; import org.h2.mvstore.db.TransactionStore2.Transaction;
import org.h2.result.Row; import org.h2.result.Row;
import org.h2.result.SortOrder; import org.h2.result.SortOrder;
import org.h2.schema.SchemaObject; import org.h2.schema.SchemaObject;
...@@ -48,7 +48,7 @@ import org.h2.value.Value; ...@@ -48,7 +48,7 @@ import org.h2.value.Value;
public class MVTable extends TableBase { public class MVTable extends TableBase {
private final String storeName; private final String storeName;
private final TransactionStore store; private final TransactionStore2 store;
private MVPrimaryIndex primaryIndex; private MVPrimaryIndex primaryIndex;
private ArrayList<Index> indexes = New.arrayList(); private ArrayList<Index> indexes = New.arrayList();
private long lastModificationId; private long lastModificationId;
...@@ -68,7 +68,7 @@ public class MVTable extends TableBase { ...@@ -68,7 +68,7 @@ public class MVTable extends TableBase {
*/ */
private boolean waitForLock; private boolean waitForLock;
public MVTable(CreateTableData data, String storeName, TransactionStore store) { public MVTable(CreateTableData data, String storeName, TransactionStore2 store) {
super(data); super(data);
nextAnalyze = database.getSettings().analyzeAuto; nextAnalyze = database.getSettings().analyzeAuto;
this.storeName = storeName; this.storeName = storeName;
...@@ -571,7 +571,8 @@ public class MVTable extends TableBase { ...@@ -571,7 +571,8 @@ public class MVTable extends TableBase {
nextAnalyze = n; nextAnalyze = n;
} }
int rows = session.getDatabase().getSettings().analyzeSample; int rows = session.getDatabase().getSettings().analyzeSample;
Analyze.analyzeTable(session, this, rows, false); int test;
// Analyze.analyzeTable(session, this, rows, false);
} }
@Override @Override
...@@ -686,7 +687,7 @@ public class MVTable extends TableBase { ...@@ -686,7 +687,7 @@ public class MVTable extends TableBase {
return session.getTransaction(store); return session.getTransaction(store);
} }
public TransactionStore getStore() { public TransactionStore2 getStore() {
return store; return store;
} }
......
...@@ -142,12 +142,12 @@ public class MVTableEngine implements TableEngine { ...@@ -142,12 +142,12 @@ public class MVTableEngine implements TableEngine {
/** /**
* The transaction store. * The transaction store.
*/ */
private final TransactionStore transactionStore; private final TransactionStore2 transactionStore;
public Store(Database db, MVStore store) { public Store(Database db, MVStore store) {
this.db = db; this.db = db;
this.store = store; this.store = store;
this.transactionStore = new TransactionStore(store, this.transactionStore = new TransactionStore2(store,
new ValueDataType(null, null, null)); new ValueDataType(null, null, null));
} }
...@@ -155,7 +155,7 @@ public class MVTableEngine implements TableEngine { ...@@ -155,7 +155,7 @@ public class MVTableEngine implements TableEngine {
return store; return store;
} }
public TransactionStore getTransactionStore() { public TransactionStore2 getTransactionStore() {
return transactionStore; return transactionStore;
} }
......
...@@ -49,7 +49,7 @@ public class TransactionStore2 { ...@@ -49,7 +49,7 @@ public class TransactionStore2 {
/** /**
* The undo log. * The undo log.
* Key: [ transactionId, logId ], value: [ baseVersion, mapId, key, oldValue ]. * Key: [ transactionId, logId ], value: [ opType, mapId, key, oldValue ].
*/ */
final MVMap<long[], Object[]> undoLog; final MVMap<long[], Object[]> undoLog;
...@@ -90,7 +90,8 @@ public class TransactionStore2 { ...@@ -90,7 +90,8 @@ public class TransactionStore2 {
// commit could be faster if we have one undo log per transaction, // commit could be faster if we have one undo log per transaction,
// or a range delete operation for maps // or a range delete operation for maps
ArrayType valueType = new ArrayType(new DataType[]{ ArrayType valueType = new ArrayType(new DataType[]{
new ObjectDataType(), new ObjectDataType(), keyType new ObjectDataType(), new ObjectDataType(), keyType,
new ObjectDataType()
}); });
MVMapConcurrent.Builder<long[], Object[]> builder = MVMapConcurrent.Builder<long[], Object[]> builder =
new MVMapConcurrent.Builder<long[], Object[]>(). new MVMapConcurrent.Builder<long[], Object[]>().
...@@ -153,7 +154,6 @@ public class TransactionStore2 { ...@@ -153,7 +154,6 @@ public class TransactionStore2 {
* @return the transaction * @return the transaction
*/ */
public synchronized Transaction begin() { public synchronized Transaction begin() {
store.incrementVersion();
long transactionId = lastTransactionId++; long transactionId = lastTransactionId++;
int status = Transaction.STATUS_OPEN; int status = Transaction.STATUS_OPEN;
return new Transaction(this, transactionId, status, null, 0); return new Transaction(this, transactionId, status, null, 0);
...@@ -215,7 +215,6 @@ public class TransactionStore2 { ...@@ -215,7 +215,6 @@ public class TransactionStore2 {
Object[] old = openTransactions.get(t.getId()); Object[] old = openTransactions.get(t.getId());
Object[] v = { old[0], name }; Object[] v = { old[0], name };
openTransactions.put(t.getId(), v); openTransactions.put(t.getId(), v);
store.commit();
} }
/** /**
...@@ -225,7 +224,6 @@ public class TransactionStore2 { ...@@ -225,7 +224,6 @@ public class TransactionStore2 {
* @param maxLogId the last log id * @param maxLogId the last log id
*/ */
void commit(Transaction t, long maxLogId) { void commit(Transaction t, long maxLogId) {
store.incrementVersion();
for (long logId = 0; logId < maxLogId; logId++) { for (long logId = 0; logId < maxLogId; logId++) {
long[] undoKey = new long[] { long[] undoKey = new long[] {
t.getId(), logId }; t.getId(), logId };
...@@ -250,15 +248,6 @@ public class TransactionStore2 { ...@@ -250,15 +248,6 @@ public class TransactionStore2 {
} }
openTransactions.remove(t.getId()); openTransactions.remove(t.getId());
openTransactionMap.remove(t.getId()); openTransactionMap.remove(t.getId());
store.commit();
long oldestVersion = store.getCurrentVersion();
for (Transaction u : openTransactionMap.values()) {
long v = u.startVersion;
if (v < oldestVersion) {
oldestVersion = v;
}
}
store.setRetainVersion(oldestVersion);
} }
/** /**
...@@ -271,7 +260,6 @@ public class TransactionStore2 { ...@@ -271,7 +260,6 @@ public class TransactionStore2 {
rollbackTo(t, maxLogId, 0); rollbackTo(t, maxLogId, 0);
openTransactions.remove(t.getId()); openTransactions.remove(t.getId());
openTransactionMap.remove(t.getId()); openTransactionMap.remove(t.getId());
store.commit();
} }
/** /**
...@@ -282,7 +270,6 @@ public class TransactionStore2 { ...@@ -282,7 +270,6 @@ public class TransactionStore2 {
* @param toLogId the log id to roll back to * @param toLogId the log id to roll back to
*/ */
void rollbackTo(Transaction t, long maxLogId, long toLogId) { void rollbackTo(Transaction t, long maxLogId, long toLogId) {
store.incrementVersion();
for (long logId = maxLogId - 1; logId >= toLogId; logId--) { for (long logId = maxLogId - 1; logId >= toLogId; logId--) {
Object[] op = undoLog.get(new long[] { Object[] op = undoLog.get(new long[] {
t.getId(), logId }); t.getId(), logId });
...@@ -293,8 +280,6 @@ public class TransactionStore2 { ...@@ -293,8 +280,6 @@ public class TransactionStore2 {
MVMap<Object, Object[]> map = store.openMap(mapName); MVMap<Object, Object[]> map = store.openMap(mapName);
Object key = op[2]; Object key = op[2];
Object[] oldValue = (Object[]) op[3]; Object[] oldValue = (Object[]) op[3];
Object[] value = map.get(key);
if (value != null) {
if (oldValue == null) { if (oldValue == null) {
// this transaction added the value // this transaction added the value
map.remove(key); map.remove(key);
...@@ -302,10 +287,8 @@ public class TransactionStore2 { ...@@ -302,10 +287,8 @@ public class TransactionStore2 {
// this transaction updated the value // this transaction updated the value
map.put(key, oldValue); map.put(key, oldValue);
} }
undoLog.remove(op);
} }
undoLog.remove(logId);
}
store.commit();
} }
/** /**
...@@ -346,14 +329,14 @@ public class TransactionStore2 { ...@@ -346,14 +329,14 @@ public class TransactionStore2 {
/** /**
* The transaction id. * The transaction id.
*/ */
final long transactionId; final Long transactionId;
long logId;
private int status; private int status;
private String name; private String name;
private long logId;
Transaction(TransactionStore2 store, long transactionId, int status, String name, long logId) { Transaction(TransactionStore2 store, long transactionId, int status, String name, long logId) {
this.store = store; this.store = store;
this.startVersion = store.store.getCurrentVersion(); this.startVersion = store.store.getCurrentVersion();
...@@ -368,7 +351,7 @@ public class TransactionStore2 { ...@@ -368,7 +351,7 @@ public class TransactionStore2 {
* *
* @return the transaction id * @return the transaction id
*/ */
public long getId() { public Long getId() {
return transactionId; return transactionId;
} }
...@@ -408,7 +391,6 @@ public class TransactionStore2 { ...@@ -408,7 +391,6 @@ public class TransactionStore2 {
*/ */
public long setSavepoint() { public long setSavepoint() {
checkOpen(); checkOpen();
store.store.incrementVersion();
return logId; return logId;
} }
...@@ -425,7 +407,7 @@ public class TransactionStore2 { ...@@ -425,7 +407,7 @@ public class TransactionStore2 {
} }
/** /**
* Open a data map where reads are always up to date. * Open a data map.
* *
* @param <K> the key type * @param <K> the key type
* @param <V> the value type * @param <V> the value type
...@@ -433,22 +415,9 @@ public class TransactionStore2 { ...@@ -433,22 +415,9 @@ public class TransactionStore2 {
* @return the transaction map * @return the transaction map
*/ */
public <K, V> TransactionMap<K, V> openMap(String name) { public <K, V> TransactionMap<K, V> openMap(String name) {
return openMap(name, -1);
}
/**
* Open a data map where reads are based on the specified version / savepoint.
*
* @param <K> the key type
* @param <V> the value type
* @param name the name of the map
* @param readVersion the version used for reading
* @return the transaction map
*/
public <K, V> TransactionMap<K, V> openMap(String name, long readVersion) {
checkOpen(); checkOpen();
return new TransactionMap<K, V>(this, name, new ObjectDataType(), return new TransactionMap<K, V>(this, name, new ObjectDataType(),
new ObjectDataType(), readVersion); new ObjectDataType());
} }
/** /**
...@@ -457,11 +426,10 @@ public class TransactionStore2 { ...@@ -457,11 +426,10 @@ public class TransactionStore2 {
* @param <K> the key type * @param <K> the key type
* @param <V> the value type * @param <V> the value type
* @param name the name of the map * @param name the name of the map
* @param readVersion the version used for reading
* @param builder the builder * @param builder the builder
* @return the transaction map * @return the transaction map
*/ */
public <K, V> TransactionMap<K, V> openMap(String name, long readVersion, Builder<K, V> builder) { public <K, V> TransactionMap<K, V> openMap(String name, Builder<K, V> builder) {
checkOpen(); checkOpen();
DataType keyType = builder.getKeyType(); DataType keyType = builder.getKeyType();
if (keyType == null) { if (keyType == null) {
...@@ -471,7 +439,7 @@ public class TransactionStore2 { ...@@ -471,7 +439,7 @@ public class TransactionStore2 {
if (valueType == null) { if (valueType == null) {
valueType = new ObjectDataType(); valueType = new ObjectDataType();
} }
return new TransactionMap<K, V>(this, name, keyType, valueType, readVersion); return new TransactionMap<K, V>(this, name, keyType, valueType);
} }
/** /**
...@@ -549,34 +517,55 @@ public class TransactionStore2 { ...@@ -549,34 +517,55 @@ public class TransactionStore2 {
* Key: key the key of the data. * Key: key the key of the data.
* Value: { transactionId, oldVersion, value } * Value: { transactionId, oldVersion, value }
*/ */
private final MVMap<K, Object[]> mapWrite; private final MVMap<K, Object[]> map;
/** /**
* The map used for reading (possibly an older version). Reading is done * If a record was read that was updated by this transaction, and the
* on an older version so that changes are not immediately visible, to * update occurred before this log id, the older version is read. This
* support statement processing (for example * is so that changes are not immediately visible, to support statement
* "update test set id = id + 1"). * processing (for example "update test set id = id + 1").
* <p>
* Key: key the key of the data.
* Value: { transactionId, oldVersion, value }
*/ */
private final MVMap<K, Object[]> mapRead; private long readLogId = Long.MAX_VALUE;
TransactionMap(Transaction transaction, String name, DataType keyType, TransactionMap(Transaction transaction, String name, DataType keyType,
DataType valueType, long readVersion) { DataType valueType) {
this.transaction = transaction; this.transaction = transaction;
ArrayType arrayType = new ArrayType(new DataType[] { ArrayType arrayType = new ArrayType(new DataType[] {
new ObjectDataType(), new ObjectDataType(), valueType new ObjectDataType(), new ObjectDataType(), valueType
}); });
MVMap.Builder<K, Object[]> builder = new MVMap.Builder<K, Object[]>() MVMap.Builder<K, Object[]> builder = new MVMap.Builder<K, Object[]>()
.keyType(keyType).valueType(arrayType); .keyType(keyType).valueType(arrayType);
mapWrite = transaction.store.store.openMap(name, builder); map = transaction.store.store.openMap(name, builder);
mapId = mapWrite.getId(); mapId = map.getId();
if (readVersion >= 0) {
mapRead = mapWrite.openVersion(readVersion);
} else {
mapRead = mapWrite;
} }
private TransactionMap(Transaction transaction, MVMap<K, Object[]> map, int mapId) {
this.transaction = transaction;
this.map = map;
this.mapId = mapId;
}
/**
* Set the savepoint. Afterwards, reads are based on the specified
* savepoint.
*
* @param savepoint the savepoint
*/
public void setSavepoint(long savepoint) {
this.readLogId = savepoint;
}
/**
* Get a clone of this map for the given transaction.
*
* @param transaction the transaction
* @param savepoint the savepoint
* @return the map
*/
public TransactionMap<K, V> getInstance(Transaction transaction, long savepoint) {
TransactionMap<K, V> m = new TransactionMap<K, V>(transaction, map, mapId);
m.setSavepoint(savepoint);
return m;
} }
/** /**
...@@ -587,7 +576,7 @@ public class TransactionStore2 { ...@@ -587,7 +576,7 @@ public class TransactionStore2 {
public long getSize() { public long getSize() {
// TODO this method is very slow // TODO this method is very slow
long size = 0; long size = 0;
Cursor<K> cursor = mapRead.keyIterator(null); Cursor<K> cursor = map.keyIterator(null);
while (cursor.hasNext()) { while (cursor.hasNext()) {
K key = cursor.next(); K key = cursor.next();
if (get(key) != null) { if (get(key) != null) {
...@@ -701,11 +690,10 @@ public class TransactionStore2 { ...@@ -701,11 +690,10 @@ public class TransactionStore2 {
* @return true if the value was set * @return true if the value was set
*/ */
public boolean trySet(K key, V value, boolean onlyIfUnchanged) { public boolean trySet(K key, V value, boolean onlyIfUnchanged) {
MVMap<K, Object[]> m = mapRead; Object[] current = map.get(key);
Object[] current = mapWrite.get(key);
if (onlyIfUnchanged) { if (onlyIfUnchanged) {
Object[] old = m.get(key); Object[] old = getArray(key, readLogId);
if (!mapWrite.areValuesEqual(old, current)) { if (!map.areValuesEqual(old, current)) {
long tx = (Long) current[0]; long tx = (Long) current[0];
if (tx == transaction.transactionId) { if (tx == transaction.transactionId) {
if (value == null) { if (value == null) {
...@@ -724,7 +712,6 @@ public class TransactionStore2 { ...@@ -724,7 +712,6 @@ public class TransactionStore2 {
} }
} }
} }
long oldVersion = transaction.store.store.getCurrentVersion() - 1;
int opType; int opType;
if (current == null || current[2] == null) { if (current == null || current[2] == null) {
if (value == null) { if (value == null) {
...@@ -740,11 +727,12 @@ public class TransactionStore2 { ...@@ -740,11 +727,12 @@ public class TransactionStore2 {
opType = Transaction.OP_SET; opType = Transaction.OP_SET;
} }
} }
Object[] newValue = { transaction.transactionId, oldVersion, value }; Object[] newValue = {
transaction.transactionId,
transaction.logId, value };
if (current == null) { if (current == null) {
// a new value // a new value
newValue[1] = null; Object[] old = map.putIfAbsent(key, newValue);
Object[] old = mapWrite.putIfAbsent(key, newValue);
if (old == null) { if (old == null) {
transaction.log(opType, mapId, key, current); transaction.log(opType, mapId, key, current);
return true; return true;
...@@ -754,15 +742,8 @@ public class TransactionStore2 { ...@@ -754,15 +742,8 @@ public class TransactionStore2 {
long tx = (Long) current[0]; long tx = (Long) current[0];
if (tx == transaction.transactionId) { if (tx == transaction.transactionId) {
// added or updated by this transaction // added or updated by this transaction
if (mapWrite.replace(key, current, newValue)) { if (map.replace(key, current, newValue)) {
if (current[1] == null) {
transaction.log(opType, mapId, key, current); transaction.log(opType, mapId, key, current);
} else {
long c = (Long) current[1];
if (c != oldVersion) {
transaction.log(opType, mapId, key, current);
}
}
return true; return true;
} }
// strange, somebody overwrite the value // strange, somebody overwrite the value
...@@ -774,7 +755,7 @@ public class TransactionStore2 { ...@@ -774,7 +755,7 @@ public class TransactionStore2 {
if (!open) { if (!open) {
// the transaction is committed: // the transaction is committed:
// overwrite the value // overwrite the value
if (mapWrite.replace(key, current, newValue)) { if (map.replace(key, current, newValue)) {
transaction.log(opType, mapId, key, current); transaction.log(opType, mapId, key, current);
return true; return true;
} }
...@@ -792,7 +773,7 @@ public class TransactionStore2 { ...@@ -792,7 +773,7 @@ public class TransactionStore2 {
* @return the value or null * @return the value or null
*/ */
public V get(K key) { public V get(K key) {
return get(key, mapRead); return get(key, readLogId);
} }
/** /**
...@@ -802,7 +783,7 @@ public class TransactionStore2 { ...@@ -802,7 +783,7 @@ public class TransactionStore2 {
* @return the value or null * @return the value or null
*/ */
public V getLatest(K key) { public V getLatest(K key) {
return get(key, mapWrite); return get(key, Long.MAX_VALUE);
} }
/** /**
...@@ -819,41 +800,46 @@ public class TransactionStore2 { ...@@ -819,41 +800,46 @@ public class TransactionStore2 {
* Get the value for the given key. * Get the value for the given key.
* *
* @param key the key * @param key the key
* @param m the map * @param maxLogId the maximum log id
* @return the value or null * @return the value or null
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public V get(K key, MVMap<K, Object[]> m) { public V get(K key, long maxLogId) {
checkOpen(); checkOpen();
Object[] data = getArray(key, maxLogId);
return data == null ? null : (V) data[2];
}
private Object[] getArray(K key, long maxLog) {
Object[] data = map.get(key);
while (true) { while (true) {
Object[] data = m.get(key);
long tx; long tx;
if (data == null) { if (data == null) {
// doesn't exist or deleted by a committed transaction // doesn't exist or deleted by a committed transaction
return null; return null;
} }
tx = (Long) data[0]; tx = (Long) data[0];
long logId = (Long) data[1];
if (tx == transaction.transactionId) { if (tx == transaction.transactionId) {
// added by this transaction // added by this transaction
return (V) data[2]; if (logId < maxLog) {
return data;
}
} }
// added or updated by another transaction // added or updated by another transaction
boolean open = transaction.store.openTransactions.containsKey(tx); boolean open = transaction.store.openTransactions.containsKey(tx);
if (!open) { if (!open) {
// it is committed // it is committed
return (V) data[2]; return data;
} }
tx = (Long) data[0];
// get the value before the uncommitted transaction // get the value before the uncommitted transaction
if (data[1] == null) { long[] x = new long[] { tx, logId };
// a new entry data = transaction.store.undoLog.get(x);
return null; data = (Object[]) data[3];
}
long oldVersion = (Long) data[1];
m = mapWrite.openVersion(oldVersion);
} }
} }
/** /**
* Rename the map. * Rename the map.
* *
...@@ -861,7 +847,7 @@ public class TransactionStore2 { ...@@ -861,7 +847,7 @@ public class TransactionStore2 {
*/ */
public void renameMap(String newMapName) { public void renameMap(String newMapName) {
// TODO rename maps transactionally // TODO rename maps transactionally
mapWrite.renameMap(newMapName); map.renameMap(newMapName);
} }
/** /**
...@@ -870,7 +856,7 @@ public class TransactionStore2 { ...@@ -870,7 +856,7 @@ public class TransactionStore2 {
* @return true if closed * @return true if closed
*/ */
public boolean isClosed() { public boolean isClosed() {
return mapWrite.isClosed(); return map.isClosed();
} }
/** /**
...@@ -878,7 +864,7 @@ public class TransactionStore2 { ...@@ -878,7 +864,7 @@ public class TransactionStore2 {
*/ */
public void removeMap() { public void removeMap() {
// TODO remove in a transaction // TODO remove in a transaction
mapWrite.removeMap(); map.removeMap();
} }
/** /**
...@@ -886,7 +872,7 @@ public class TransactionStore2 { ...@@ -886,7 +872,7 @@ public class TransactionStore2 {
*/ */
public void clear() { public void clear() {
// TODO truncate transactionally // TODO truncate transactionally
mapWrite.clear(); map.clear();
} }
/** /**
...@@ -896,7 +882,7 @@ public class TransactionStore2 { ...@@ -896,7 +882,7 @@ public class TransactionStore2 {
*/ */
public K firstKey() { public K firstKey() {
// TODO transactional firstKey // TODO transactional firstKey
return mapRead.firstKey(); return map.firstKey();
} }
/** /**
...@@ -906,7 +892,7 @@ public class TransactionStore2 { ...@@ -906,7 +892,7 @@ public class TransactionStore2 {
*/ */
public K lastKey() { public K lastKey() {
// TODO transactional lastKey // TODO transactional lastKey
return mapRead.lastKey(); return map.lastKey();
} }
/** /**
...@@ -917,7 +903,7 @@ public class TransactionStore2 { ...@@ -917,7 +903,7 @@ public class TransactionStore2 {
*/ */
public Iterator<K> keyIterator(K from) { public Iterator<K> keyIterator(K from) {
// TODO transactional keyIterator // TODO transactional keyIterator
return mapRead.keyIterator(from); return map.keyIterator(from);
} }
/** /**
...@@ -928,7 +914,7 @@ public class TransactionStore2 { ...@@ -928,7 +914,7 @@ public class TransactionStore2 {
*/ */
public K ceilingKey(K key) { public K ceilingKey(K key) {
// TODO transactional ceilingKey // TODO transactional ceilingKey
return mapRead.ceilingKey(key); return map.ceilingKey(key);
} }
/** /**
...@@ -940,7 +926,7 @@ public class TransactionStore2 { ...@@ -940,7 +926,7 @@ public class TransactionStore2 {
*/ */
public K higherKey(K key) { public K higherKey(K key) {
// TODO transactional higherKey // TODO transactional higherKey
return mapRead.higherKey(key); return map.higherKey(key);
} }
/** /**
...@@ -952,7 +938,7 @@ public class TransactionStore2 { ...@@ -952,7 +938,7 @@ public class TransactionStore2 {
*/ */
public K lowerKey(K key) { public K lowerKey(K key) {
// TODO Auto-generated method stub // TODO Auto-generated method stub
return mapRead.lowerKey(key); return map.lowerKey(key);
} }
public Transaction getTransaction() { public Transaction getTransaction() {
......
...@@ -164,4 +164,8 @@ public class Row implements SearchRow { ...@@ -164,4 +164,8 @@ public class Row implements SearchRow {
return deleted; return deleted;
} }
public Value[] getValueList() {
return data;
}
} }
...@@ -17,6 +17,7 @@ import org.h2.engine.Constants; ...@@ -17,6 +17,7 @@ import org.h2.engine.Constants;
import org.h2.mvstore.db.MVTableEngine; import org.h2.mvstore.db.MVTableEngine;
import org.h2.store.fs.FileUtils; import org.h2.store.fs.FileUtils;
import org.h2.test.TestBase; import org.h2.test.TestBase;
import org.h2.util.Profiler;
import org.h2.util.Task; import org.h2.util.Task;
/** /**
...@@ -38,23 +39,26 @@ public class TestMVTableEngine extends TestBase { ...@@ -38,23 +39,26 @@ public class TestMVTableEngine extends TestBase {
testEncryption(); testEncryption();
testReadOnly(); testReadOnly();
testReuseDiskSpace(); testReuseDiskSpace();
testDataTypes(); // testDataTypes();
testLocking(); testLocking();
testSimple(); // testSimple();
} }
private void testSpeed() throws Exception { private void testSpeed() throws Exception {
String dbName; String dbName;
for (int i = 0; i < 5; i++) { for (int i = 0; i < 10; i++) {
dbName = "mvstore"; dbName = "mvstore";
dbName += ";LOCK_MODE=0"; // dbName += ";LOCK_MODE=0";
testSpeed(dbName); testSpeed(dbName);
// Profiler prof = new Profiler().startCollecting(); int tes;
//Profiler prof = new Profiler().startCollecting();
dbName = "mvstore" + dbName = "mvstore" +
";DEFAULT_TABLE_ENGINE=org.h2.mvstore.db.MVTableEngine"; ";DEFAULT_TABLE_ENGINE=org.h2.mvstore.db.MVTableEngine";
// dbName += ";LOCK_MODE=0";
testSpeed(dbName); testSpeed(dbName);
// System.out.println(prof.getTop(10)); //System.out.println(prof.getTop(10));
} }
FileUtils.deleteRecursive(getBaseDir(), true);
} }
private void testSpeed(String dbName) throws Exception { private void testSpeed(String dbName) throws Exception {
...@@ -69,15 +73,14 @@ public class TestMVTableEngine extends TestBase { ...@@ -69,15 +73,14 @@ public class TestMVTableEngine extends TestBase {
stat.execute("create table test(id int primary key, name varchar(255))"); stat.execute("create table test(id int primary key, name varchar(255))");
PreparedStatement prep = conn PreparedStatement prep = conn
.prepareStatement("insert into test values(?, ?)"); .prepareStatement("insert into test values(?, ?)");
prep.setString(2, "Hello World"); prep.setString(2, "Hello World xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) { for (int i = 0; i < 200000; i++) {
prep.setInt(1, i); prep.setInt(1, i);
prep.execute(); prep.execute();
} }
System.out.println((System.currentTimeMillis() - time) + " " + dbName); System.out.println((System.currentTimeMillis() - time) + " " + dbName);
conn.close(); conn.close();
FileUtils.deleteRecursive(getBaseDir(), true);
} }
private void testEncryption() throws Exception { private void testEncryption() throws Exception {
...@@ -375,7 +378,7 @@ public class TestMVTableEngine extends TestBase { ...@@ -375,7 +378,7 @@ public class TestMVTableEngine extends TestBase {
stat.execute("insert into test(id, name) values(10, 'Hello')"); stat.execute("insert into test(id, name) values(10, 'Hello')");
fail(); fail();
} catch (SQLException e) { } catch (SQLException e) {
assertEquals(ErrorCode.DUPLICATE_KEY_1, e.getErrorCode()); assertEquals(e.toString(), ErrorCode.DUPLICATE_KEY_1, e.getErrorCode());
} }
rs = stat.executeQuery("select min(id), max(id), min(name), max(name) from test"); rs = stat.executeQuery("select min(id), max(id), min(name), max(name) from test");
......
...@@ -64,21 +64,19 @@ public class TestTransactionStore extends TestBase { ...@@ -64,21 +64,19 @@ public class TestTransactionStore extends TestBase {
Transaction tx; Transaction tx;
TransactionMap<String, String> m; TransactionMap<String, String> m;
long startUpdate; long startUpdate;
long version;
tx = ts.begin(); tx = ts.begin();
// start of statement // start of statement
// create table test // create table test
startUpdate = tx.setSavepoint(); startUpdate = tx.setSavepoint();
version = s.getCurrentVersion(); m = tx.openMap("test");
tx.openMap("test", version); m.setSavepoint(startUpdate);
// start of statement // start of statement
// insert into test(id, name) values(1, 'Hello'), (2, 'World') // insert into test(id, name) values(1, 'Hello'), (2, 'World')
startUpdate = tx.setSavepoint(); startUpdate = tx.setSavepoint();
version = s.getCurrentVersion(); m.setSavepoint(startUpdate);
m = tx.openMap("test", version);
assertTrue(m.trySet("1", "Hello", true)); assertTrue(m.trySet("1", "Hello", true));
assertTrue(m.trySet("2", "World", true)); assertTrue(m.trySet("2", "World", true));
// not seen yet (within the same statement) // not seen yet (within the same statement)
...@@ -87,13 +85,12 @@ public class TestTransactionStore extends TestBase { ...@@ -87,13 +85,12 @@ public class TestTransactionStore extends TestBase {
// start of statement // start of statement
startUpdate = tx.setSavepoint(); startUpdate = tx.setSavepoint();
version = s.getCurrentVersion();
// now we see the newest version // now we see the newest version
m = tx.openMap("test", version); m.setSavepoint(startUpdate);
assertEquals("Hello", m.get("1")); assertEquals("Hello", m.get("1"));
assertEquals("World", m.get("2")); assertEquals("World", m.get("2"));
// update test set primaryKey = primaryKey + 1 // update test set primaryKey = primaryKey + 1
// (this is usually a tricky cases) // (this is usually a tricky case)
assertEquals("Hello", m.get("1")); assertEquals("Hello", m.get("1"));
assertTrue(m.trySet("1", null, true)); assertTrue(m.trySet("1", null, true));
assertTrue(m.trySet("2", "Hello", true)); assertTrue(m.trySet("2", "Hello", true));
...@@ -110,8 +107,7 @@ public class TestTransactionStore extends TestBase { ...@@ -110,8 +107,7 @@ public class TestTransactionStore extends TestBase {
// start of statement // start of statement
startUpdate = tx.setSavepoint(); startUpdate = tx.setSavepoint();
version = s.getCurrentVersion(); m.setSavepoint(startUpdate);
m = tx.openMap("test", version);
// select * from test // select * from test
assertNull(m.get("1")); assertNull(m.get("1"));
assertEquals("Hello", m.get("2")); assertEquals("Hello", m.get("2"));
...@@ -119,8 +115,7 @@ public class TestTransactionStore extends TestBase { ...@@ -119,8 +115,7 @@ public class TestTransactionStore extends TestBase {
// start of statement // start of statement
startUpdate = tx.setSavepoint(); startUpdate = tx.setSavepoint();
version = s.getCurrentVersion(); m.setSavepoint(startUpdate);
m = tx.openMap("test", version);
// update test set id = 1 // update test set id = 1
// should fail: duplicate key // should fail: duplicate key
assertTrue(m.trySet("2", null, true)); assertTrue(m.trySet("2", null, true));
...@@ -129,8 +124,8 @@ public class TestTransactionStore extends TestBase { ...@@ -129,8 +124,8 @@ public class TestTransactionStore extends TestBase {
assertFalse(m.trySet("1", "World", true)); assertFalse(m.trySet("1", "World", true));
tx.rollbackToSavepoint(startUpdate); tx.rollbackToSavepoint(startUpdate);
version = s.getCurrentVersion(); startUpdate = tx.setSavepoint();
m = tx.openMap("test", version); m.setSavepoint(startUpdate);
assertNull(m.get("1")); assertNull(m.get("1"));
assertEquals("Hello", m.get("2")); assertEquals("Hello", m.get("2"));
assertEquals("World", m.get("3")); assertEquals("World", m.get("3"));
......
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论