Azure service fabric 具有100万个密钥的Service fabric可靠字典性能

Azure service fabric 具有100万个密钥的Service fabric可靠字典性能,azure-service-fabric,Azure Service Fabric,我正在使用约100万个密钥的可靠字典评估服务结构的性能。我得到了相当令人失望的结果,所以我想检查我的代码或预期是否有误 我有一本用字母初始化的字典 dict=await\u stateManager.GetOrAddAsync(“测试”+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".... 多达576000件物品。字典中的值没有被使用,我目前只使用“1”

我正在使用约100万个密钥的可靠字典评估服务结构的性能。我得到了相当令人失望的结果,所以我想检查我的代码或预期是否有误

我有一本用字母初始化的字典
dict=await\u stateManager.GetOrAddAsync(“测试”+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".... 多达576000件物品。字典中的值没有被使用,我目前只使用“1”

将所有项目添加到字典大约需要3分钟。我必须一次将事务拆分为100000,否则它似乎永远挂起(在需要
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秒

我不太关心写入时间,我知道它必须被复制和持久化。但是为什么要花这么长时间阅读呢?576000 17个字符的字符串键在内存中不应超过11.5mb,并且这些值仅为单个字符,将被忽略。可靠的集合不是缓存在ram中吗?迭代相同值的常规字典需要13毫秒

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

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


这些结果可信吗?我应该检查什么配置?我是做错了什么,还是我的期望太不准确了?如果是,是否有更适合这些要求的产品?(~100万个小键,没有值,持续事务更新)

好的,不管它值多少钱:

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

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

  • 这取决于您拥有的资源。例如,试图用一个分区和三个副本在我的本地计算机上复制您的案例,我平均在5秒钟内得到记录

考虑到解决方案,以下是我们想到的:

  • 分块我也尝试过同样的方法,将记录分割成以10个元素为上限的字符串数组(IReliableDictionary但是时间范围从5秒减少到了7毫秒。我想如果您将项目保持在80KB以下,从而减少往返次数并保持LOH较小,您应该会看到性能有所提高

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

  • State Serializer如果超出简单字符串的范围,您可以开发自己的字符串,并尝试根据您的类型减少产生的I/O

希望它是有意义的。

您能使用像AddRange这样的命令吗?在它之后是CommitAsync吗?我有类似的经验与经典数据库。。。如果单独保存更多的实体-时间比AddRange命令长得多…IReliableDictionary没有这样的AddRange批量插入方法,但我更关心的是读取速度,而不是初始插入。请参阅此参考:您不仅读取数据,而且还读取数据。请仅检查读取数据并测量延迟:IAsyncEnumerable enumerable=Wait dictionary.CreateEnumerableAsync(tx);谢谢我得到的数据将被分页到磁盘,但在我的情况下,我有小键,希望忽略
value
s。如果有一个
集合
类型就好了!11mb的密钥应该没什么大不了的。感谢您亲自尝试并获得5s,了解这一点很有用。因为我的密钥是可预测的,所以我应该能够将它们分为具有可预测名称的存储桶(80kb约400项=576000约1400项),同时编辑同一个存储桶时可能会有更多的写死锁。@rockgecko我很高兴这对您有所帮助。你能把你的桶放在演员身上吗?由于演员的天性是线程安全的,即使在成千上万的演员被创造出来的时候,他们也工作得很好,所以你可以尝试他们的表演。此外,如果在整个SF集群崩溃的情况下丢失集合中的数据对您来说是可以的,那么您可以将参与者设置为仅将所有数据保留在内存中(易失性状态)。否则,如果Actors impl不假设在Actor激活时从磁盘加载所有状态,您可以模拟上面提到的Set类型。
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;
    }
}