我正在使用大约100万个密钥的可靠字典来评估Service Fabric的性能。我得到了相当令人失望的结果,所以我想检查我的代码或我的期望是否错误。
我有一个初始化的字典
dict = await _stateManager.GetOrAddAsync<IReliableDictionary2<string, string>>("test_"+id);
id
对于每次测试运行都是唯一的。
我用一个字符串列表填充它,比如 “1-1-1-1-1-1-1-1-1” “1-1-1-1-1-1-1-1-2” “1-1-1-1-1-1-1-1-3”......最多576,000项。字典中的值未使用,我目前只使用“1”。
将所有项目添加到词典大约需要3分钟。我必须一次将事务拆分为100,000,否则它似乎永远挂起(在您需要CommitAsync()
之前,事务中的操作数是否有限制?)
//take100_000 is the next 100_000 in the original list of 576,000
using (var tx = _stateManager.CreateTransaction())
{
foreach (var tick in take100_000) {
await dict.AddAsync(tx, tick, "1");
}
await tx.CommitAsync();
}
之后,我需要遍历字典来访问每个项目:
using (var tx = _stateManager.CreateTransaction())
{
var enumerator = (await dict.CreateEnumerableAsync(tx)).GetAsyncEnumerator();
try
{
while (await enumerator.MoveNextAsync(ct))
{
var tick = enumerator.Current.Key;
//do something with tick
}
}
catch (Exception ex)
{
throw ex;
}
}
这需要16秒。
我不是那么关心写时间,我知道它必须被复制和持久化。但为什么阅读需要这么长时间?内存中576,000个17个字符的字符串键不应超过11.5mb,并且这些值只是一个字符并被忽略。是不是可靠的集合缓存在ram中?迭代相同值的常规字典需要13ms。
然后我在一个空字典上调用了ContainsKeyAsync
576,000次(在1个事务中)。这花了112秒。尝试使用任何其他数据结构可能需要~0 ms。
这是在本地1节点集群上。部署到Azure时,我得到了类似的结果。
这些结果是否合理?我应该检查的任何配置?我做错了什么,还是我的期望非常不准确?如果是这样,是否有更适合这些要求的东西? (约100万个小密钥,没有值,持久的事务更新)
答案 0 :(得分:2)
好的,值得的:
并非所有内容都存储在内存中。为了支持大型可靠集合,某些值被缓存,其中一些值驻留在磁盘上,这可能会导致额外的I / O,同时检索你要求的数据。我听说有传言说在某些时候我们可能有机会调整缓存策略,但我认为它已经没有实现。
您逐个遍历数据读取记录。恕我直言,如果你试图针对任何数据源发出50万个单独的顺序查询,结果将不会太乐观。我并不是说每一个MoveNext()都会产生一个单独的I / O操作,但我会说它整体上看起来不像是一次获取。
这取决于您拥有的资源。例如,尝试使用单个分区和三个副本在本地计算机上重现您的案例,我得到的记录平均为5秒。
考虑一种解决方法,请记住以下内容:
分块我试图将记录分成带有10个元素的字符串数组(IReliableDictionary&lt; string,string []&gt;)。所以基本上它是相同数量的数据,但时间范围从5秒减少到7毫秒。我想如果你把你的物品保持在80KB以下,从而减少往返次数并保持LOH小,你应该看到你的表现得到了改善。
过滤 CreateEnumerableAsync有一个重载,允许您指定一个委托,以避免从磁盘中检索与过滤器不匹配的键的值。
希望这是有道理的。