Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
570437e8
提交
570437e8
authored
12月 13, 2013
作者:
Thomas Mueller
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Improved performance for read operations.
上级
44027adc
隐藏空白字符变更
内嵌
并排
正在显示
10 个修改的文件
包含
425 行增加
和
112 行删除
+425
-112
Cursor.java
h2/src/main/org/h2/mvstore/Cursor.java
+16
-2
DataUtils.java
h2/src/main/org/h2/mvstore/DataUtils.java
+36
-0
MVMap.java
h2/src/main/org/h2/mvstore/MVMap.java
+16
-6
MVDelegateIndex.java
h2/src/main/org/h2/mvstore/db/MVDelegateIndex.java
+3
-2
MVPrimaryIndex.java
h2/src/main/org/h2/mvstore/db/MVPrimaryIndex.java
+40
-33
MVSecondaryIndex.java
h2/src/main/org/h2/mvstore/db/MVSecondaryIndex.java
+5
-4
MVSpatialIndex.java
h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java
+2
-2
TransactionStore.java
h2/src/main/org/h2/mvstore/db/TransactionStore.java
+184
-59
TestBenchmark.java
h2/src/test/org/h2/test/store/TestBenchmark.java
+119
-0
TestMVStore.java
h2/src/test/org/h2/test/store/TestMVStore.java
+4
-4
没有找到文件。
h2/src/main/org/h2/mvstore/Cursor.java
浏览文件 @
570437e8
...
...
@@ -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
;
...
...
h2/src/main/org/h2/mvstore/DataUtils.java
浏览文件 @
570437e8
...
...
@@ -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"
);
}
}
}
h2/src/main/org/h2/mvstore/MVMap.java
浏览文件 @
570437e8
...
...
@@ -745,20 +745,30 @@ public class MVMap<K, V> extends AbstractMap<K, V>
}
/**
* Iterate over a
ll
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
...
...
h2/src/main/org/h2/mvstore/db/MVDelegateIndex.java
浏览文件 @
570437e8
...
...
@@ -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
);
}
...
...
h2/src/main/org/h2/mvstore/db/MVPrimaryIndex.java
浏览文件 @
570437e8
...
...
@@ -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
)
{
l
ong
min
,
max
;
ValueL
ong
min
,
max
;
if
(
first
==
null
||
mainIndexColumn
<
0
)
{
min
=
Long
.
MIN_VALUE
;
min
=
MIN
;
}
else
{
Value
v
=
first
.
getValue
(
mainIndexColumn
);
Value
Long
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
);
Value
Long
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
.
ke
yIterator
(
ValueLong
.
get
(
min
)
),
max
);
return
new
MVStoreCursor
(
map
.
entr
yIterator
(
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
(
);
Value
Long
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
,
l
ong
ifNull
)
{
ValueLong
getKey
(
SearchRow
row
,
ValueLong
ifEmpty
,
ValueL
ong
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
,
l
ong
last
)
{
Cursor
find
(
Session
session
,
ValueLong
first
,
ValueL
ong
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
.
get
Long
()
>
last
)
{
current
=
it
.
next
();
if
(
current
!=
null
&&
current
.
get
Key
().
getLong
()
>
last
.
getLong
()
)
{
current
=
null
;
}
row
=
null
;
...
...
h2/src/main/org/h2/mvstore/db/MVSecondaryIndex.java
浏览文件 @
570437e8
...
...
@@ -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
);
}
...
...
h2/src/main/org/h2/mvstore/db/MVSpatialIndex.java
浏览文件 @
570437e8
...
...
@@ -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
);
}
...
...
h2/src/main/org/h2/mvstore/db/TransactionStore.java
浏览文件 @
570437e8
...
...
@@ -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
.
keyIterat
or
(
null
);
Cursor
<
K
,
VersionedValue
>
cursor
=
map
.
curs
or
(
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
)
{
Curs
or
<
K
>
cursor
=
map
.
keyIterator
(
key
);
Iterat
or
<
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
Curs
or
<
K
>
cursor
=
map
.
keyIterator
(
key
);
Iterat
or
<
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
)
{
Curs
or
<
K
>
it
=
map
.
keyIterator
(
from
);
Iterat
or
<
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
();
}
}
/**
...
...
h2/src/test/org/h2/test/store/TestBenchmark.java
0 → 100644
浏览文件 @
570437e8
/*
* 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();
}
}
h2/src/test/org/h2/test/store/TestMVStore.java
浏览文件 @
570437e8
...
...
@@ -706,7 +706,7 @@ public class TestMVStore extends TestBase {
map
.
put
(
i
,
10
*
i
);
}
Cursor
<
Integer
>
c
=
map
.
keyIterat
or
(
50
);
Cursor
<
Integer
,
Integer
>
c
=
map
.
curs
or
(
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
.
keyIterat
or
(
0
);
c
=
map
.
curs
or
(
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
.
keyIterat
or
(
0
);
c
=
map
.
curs
or
(
0
);
c
.
skip
(
20
);
assertEquals
(
40
,
c
.
next
().
intValue
());
c
=
map
.
keyIterat
or
(
0
);
c
=
map
.
curs
or
(
0
);
assertEquals
(
0
,
c
.
next
().
intValue
());
assertEquals
(
12
,
map
.
keyList
().
indexOf
(
24
));
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论