具有100万个密钥的服务结构可靠的字典性能

时间:2017-10-30 06:24:19

标签: azure-service-fabric

我正在使用大约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万个小密钥,没有值,持久的事务更新)

1 个答案:

答案 0 :(得分:2)

好的,值得的:

  • 并非所有内容都存储在内存中。为了支持大型可靠集合,某些值被缓存,其中一些值驻留在磁盘上,这可能会导致额外的I / O,同时检索你要求的数据。我听说有传言说在某些时候我们可能有机会调整缓存策略,但我认为它已经没有实现。

  • 您逐个遍历数据读取记录。恕我直言,如果你试图针对任何数据源发出50万个单独的顺序查询,结果将不会太乐观。我并不是说每一个MoveNext()都会产生一个单独的I / O操作,但我会说它整体上看起来不像是一次获取。

  • 这取决于您拥有的资源。例如,尝试使用单个分区和三个副本在本地计算机上重现您的案例,我得到的记录平均为5秒。

考虑一种解决方法,请记住以下内容:

  • 分块我试图将记录分成带有10个元素的字符串数组(IReliableDictionary&lt; string,string []&gt;)。所以基本上它是相同数量的数据,但时间范围从5秒减少到7毫秒。我想如果你把你的物品保持在80KB以下,从而减少往返次数并保持LOH小,你应该看到你的表现得到了改善。

  • 过滤 CreateEnumerableAsync有一个重载,允许您指定一个委托,以避免从磁盘中检索与过滤器不匹配的键的值。

  • 状态序列化程序如果您超越简单字符串,您可以开发自己的Serializer并尝试减少针对您的类型产生的I / O.

希望这是有道理的。