我们正在使用ChronicleMap来支持大量不同商店中的堆外持久性,但是使用最简单的用例会遇到一些问题。
首先,这里是我写的帮手,让创作变得更容易:
import java.io.File
import java.util.concurrent.atomic.AtomicLong
import com.madhukaraphatak.sizeof.SizeEstimator
import net.openhft.chronicle.map.{ChronicleMap, ChronicleMapBuilder}
import scala.reflect.ClassTag
object ChronicleHelper {
def estimateSizes[Key, Value](data: Iterator[(Key, Value)], keyEstimator: AnyRef => Long = defaultEstimator, valueEstimator: AnyRef => Long = defaultEstimator): (Long, Long, Long) = {
println("Estimating sizes...")
val entries = new AtomicLong(1)
val keySum = new AtomicLong(1)
val valueSum = new AtomicLong(1)
var i = 0
val GroupSize = 5000
data.grouped(GroupSize).foreach { chunk =>
chunk.par.foreach { case (key, value) =>
entries.incrementAndGet()
keySum.addAndGet(keyEstimator(key.asInstanceOf[AnyRef]))
valueSum.addAndGet(valueEstimator(value.asInstanceOf[AnyRef]))
}
i += 1
println("Progress:" + i * GroupSize)
}
(entries.get(), keySum.get() / entries.get(), valueSum.get() / entries.get())
}
def defaultEstimator(v: AnyRef): Long = SizeEstimator.estimate(v)
def createMap[Key: ClassTag, Value: ClassTag](data: => Iterator[(Key, Value)], file: File): ChronicleMap[Key, Value] = {
val keyClass = implicitly[ClassTag[Key]].runtimeClass.asInstanceOf[Class[Key]]
val valueClass = implicitly[ClassTag[Value]].runtimeClass.asInstanceOf[Class[Value]]
val (entries, averageKeySize, averageValueSize) = estimateSizes(data)
val builder = ChronicleMapBuilder.of(keyClass, valueClass)
.entries(entries)
.averageKeySize(averageKeySize)
.averageValueSize(averageValueSize)
.asInstanceOf[ChronicleMapBuilder[Key, Value]]
val cmap = builder.createPersistedTo(file)
val GroupSize = 5000
println("Inserting data...")
var i = 0
data.grouped(GroupSize).foreach { chunk =>
chunk.par.foreach { case (key, value) =>
cmap.put(key, value)
}
i += 1
println("Progress:" + i * GroupSize)
}
cmap
}
def empty[Key: ClassTag, Value: ClassTag]: ChronicleMap[Key, Value] = {
val keyClass = implicitly[ClassTag[Key]].runtimeClass.asInstanceOf[Class[Key]]
val valueClass = implicitly[ClassTag[Value]].runtimeClass.asInstanceOf[Class[Value]]
ChronicleMapBuilder.of(keyClass, valueClass).create()
}
def loadMap[Key: ClassTag, Value: ClassTag](file: File): ChronicleMap[Key, Value] = {
val keyClass = implicitly[ClassTag[Key]].runtimeClass.asInstanceOf[Class[Key]]
val valueClass = implicitly[ClassTag[Value]].runtimeClass.asInstanceOf[Class[Value]]
ChronicleMapBuilder.of(keyClass, valueClass).createPersistedTo(file)
}
}
它使用https://github.com/phatak-dev/java-sizeof进行对象大小估算。以下是我们想要支持的用法:
object TestChronicle {
def main(args: Array[String]) {
def dataIterator: Iterator[(String, Int)] = (1 to 5000).toIterator.zipWithIndex.map(x => x.copy(_1 = x._1.toString))
ChronicleHelper.createMap[String, Int](dataIterator, new File("/tmp/test.map"))
}
}
但它引发了一个例外:
[error]线程" main"中的异常java.lang.ClassCastException:Key 必须是一个int但是类java.lang.Integer [error] at net.openhft.chronicle.hash.impl.VanillaChronicleHash.checkKey(VanillaChronicleHash.java:661) [错误]在 net.openhft.chronicle.map.VanillaChronicleMap.queryContext(VanillaChronicleMap.java:281) [错误]在 net.openhft.chronicle.map.VanillaChronicleMap.put(VanillaChronicleMap.java:390) [错误]在......
我可以看到它可能与Scala的Int的原子性有关,而不是Java的Integer,但我该如何绕过它?
Scala 2.11.7
Chronicle Map 3.8.0
答案 0 :(得分:1)
Iterator[(String, Int)]
(而不是Iterator[(Int, String)]
)的密钥类型为String
且值类型为Int
,而错误消息是关于键的类型(int / Integer)Key must be a %type%
,则表示您在第一个ChronicleMapBuilder.of(keyType, valueType)
语句中配置了该类型。因此,在您的情况下,这意味着您配置了int.class
(Class
对象,表示Java中的原始int
类型),这是不允许的,并提供java.lang.Integer
实例map的方法(可能你提供原始int
,但由于拳击而变成Integer
,这是允许的。您应该确保向java.lang.Integer.class
电话提供ChronicleMapBuilder.of(keyType, valueType)
(或其他一些Scala课程)。我不知道这个项目给出了什么尺寸估算:https://github.com/phatak-dev/java-sizeof,但无论如何你应该在 bytes 中指定对象将采用的尺寸< em>序列化表格。序列化表单本身取决于默认序列化程序,为Chronicle Map中的特定类型选择(并且可能在Chronicle Map版本之间更改),或者为特定ChronicleMapBuilder
配置的自定义序列化程序。因此,使用任何有关键/值&#34;尺寸&#34;的信息。除了Chronicle Map本身之外,配置Chronicle Map是脆弱的。您可以使用以下过程更可靠地估算尺寸:
public static <V> double averageValueSize(Class<V> valueClass, Iterable<V> values) {
try (ChronicleMap<Integer, V> testMap = ChronicleMap.of(Integer.class, valueClass)
// doesn't matter, anyway not a single value will be written to a map
.averageValueSize(1)
.entries(1)
.create()) {
LongSummaryStatistics statistics = new LongSummaryStatistics();
for (V value : values) {
try (MapSegmentContext<Integer, V, ?> c = testMap.segmentContext(0)) {
statistics.accept(c.wrapValueAsData(value).size());
}
}
return statistics.getAverage();
}
}
这个程序很苛刻,但现在没有更好的选择。
另一项建议:
averageKey/averageValue/averageKeySize/averageValueSize
方法,更好您使用constantKeySizeBySample/constantValueSizeBySample
方法。特别是对于java.lang.Integer
,Long
和Double
,即使这不是必需的,Chronicle Map也已经知道这些类型的大小不断。