服务结构可靠字典并行读取

时间:2018-08-01 10:32:40

标签: c# azure-service-fabric reliable-dictionary

我有一个可靠的字典,分布在由7个节点组成的群集中。 [60个分区]。我已经设置了像这样的远程侦听器:

var settings = new FabricTransportRemotingListenerSettings
        {
            MaxMessageSize = Common.ServiceFabricGlobalConstants.MaxMessageSize,
            MaxConcurrentCalls = 200
        };

        return new[]
        {
            new ServiceReplicaListener((c) => new FabricTransportServiceRemotingListener(c, this, settings))
        };

我正在尝试进行负载测试,以证明可靠的字典“读取”性能在负载下不会降低。我从这样的字典方法中读取了一个信息:

using (ITransaction tx = this.StateManager.CreateTransaction())
        {
            IAsyncEnumerable<KeyValuePair<PriceKey, Price>> items;
            IAsyncEnumerator<KeyValuePair<PriceKey, Price>> e;

            items = await priceDictionary.CreateEnumerableAsync(tx,
                (item) => item.Id == id, EnumerationMode.Unordered);                
            e = items.GetAsyncEnumerator();

            while (await e.MoveNextAsync(CancellationToken.None))
            {
                var p = new Price(
                    e.Current.Key.Id,
                    e.Current.Key.Version, e.Current.Key.Id, e.Current.Key.Date,
                    e.Current.Value.Source, e.Current.Value.Price, e.Current.Value.Type,
                    e.Current.Value.Status);

                intermediatePrice.TryAdd(new PriceKey(e.Current.Key.Id, e.Current.Key.Version, id, e.Current.Key.Date), p);
            }
        }
return intermediatePrice;

每个分区大约有500,000条记录。字典中的每个“键”约为200个字节,“值”约为600个字节。当我直接从浏览器中调用此“读取”(调用REST API,后者又调用有状态服务)时,需要200毫秒。 如果我通过负载测试(假设有16个并行线程达到相同分区相同记录)运行此操作,则每次调用平均需要花费600毫秒。如果将负载测试并行线程数增加到24或30,则每个调用大约需要1秒。 我的问题是,服务结构可靠字典可以像SQL Server一样处理并行并发读取,而又不影响吞吐量吗?

2 个答案:

答案 0 :(得分:0)

根据代码,我看到的所有内容都是在副本上执行的-因此,您有7个节点和60个处理请求的服务实例。如果一切正常,就会有 60 个副本处理请求。

您有 7 个节点和 60 个副本-因此,如果我们认为它们在节点之间或多或少地平均分布,则每个副本有 8 个副本节点。

我不确定每个节点的物理配置,但是如果暂时假设每个节点具有4个vCPU,那么您可以想象,当您在同一节点上发出8个并发请求时,现在所有这些请求都应使用4个vCPU。这种情况导致工作线程争夺来获取资源-保持简单会大大减慢处理速度。

此效果之所以如此明显,是因为您正在扫描 IReliableDictionary,而不是像想象中的那样使用TryGetValueAsync通过按键获取项目。

您可以尝试将代码更改为使用TryGetValueAsync,两者之间的区别将非常明显。

答案 1 :(得分:0)

如果您查看关于Reliable Dictionary CreateEnumerableAsync Method的备注,您会发现它被设计为可同时工作,因此并发不是问题。

  

返回的枚举数可以安全地与reads和   写入可靠字典。它表示快照一致   查看

问题在于, 同时 并不意味着 快速

以这种方式进行查询时,它将:

  1. 必须先收集集合的快照,然后再开始处理它,否则处理时将无法对其进行写入。
  2. 您必须浏览集合中的所有值以找到所需的项目,并在返回任何内容之前记下这些值。
  3. 从磁盘中加载数据(如果尚未存储在内存中),只有“密钥”保留在内存中,不需要时将值保留在磁盘中,并且可能会分页以释放内存。
  4. 以下查询可能(我不确定,但我想)不会重用上一个查询,自上次查询以来,您的收藏集可能已更改。

当您以这种方式运行大量查询时,许多因素都会发生:

  • 磁盘:将数据加载到内存中,
  • CPU:比较值和调度线程
  • 内存:存储要处理的快照

使用“可靠字典”的最佳方法是通过“键”检索这些值,因为它确切知道特定键的数据存储在何处,并且不会增加查找这些键的额外开销。

如果您真的想以这种方式使用它,建议您像Index Table那样设计它,在其中将按id索引的数据存储在一个Dictionary中,另一把以关键字为搜索值的字典中存储,价值是主要数字的关键。这样会更快。