Service Fabric 具有 100 万个键的可靠字典性能

Service fabric reliable dictionary performance with 1 million keys

我正在使用约 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 分钟。必须把事务拆分到100000一次,不然好像永远挂了(一个事务有操作次数限制才需要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,并且值只是单个字符并被忽略。 Reliable Collections 不是缓存在 ram 中吗?遍历具有相同值的常规字典需要 13 毫秒。

然后我在一个空字典上调用了 ContainsKeyAsync 576,000 次(在 1 个事务中)。这花了 112 秒。在任何其他数据结构上尝试此操作可能需要 ~0 毫秒。

这是在本地 1 节点集群上。部署到 Azure 时我得到了类似的结果。

这些结果是否合理?我应该检查任何配置吗?我做错了什么,还是我的期望非常不准确?如果是这样,是否有更适合这些要求的东西? (约 100 万个小键,无值,持久事务更新)

好的,物有所值:

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

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

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

考虑解决方法,想到的是:

  • Chunking 我尝试做同样的事情,将记录拆分为最多包含 10 个元素的字符串数组 (IReliableDictionary< string, string[] >)。所以本质上它是相同数量的数据,但时间范围从 5 秒减少到 7 毫秒。 我想如果你将你的项目保持在 80KB 以下从而减少往返次数和保持 LOH 较小,您应该会看到您的性能有所提高。

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

  • State Serializer 如果您超越了简单的字符串,您可以开发自己的 Serializer 并尝试减少对您的类型产生的 I/O。

希望它有意义。