代码之家  ›  专栏  ›  技术社区  ›  teeboy

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

  •  0
  • teeboy  · 技术社区  · 6 年前

    我有一个可靠的字典跨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;
    

    每个分区有大约500000条记录。字典中的每个“键”大约是200字节,“值”大约是600字节。当我直接从浏览器调用这个“read”时(调用RESTAPI,后者反过来调用有状态服务),需要200毫秒。 如果我通过负载测试来运行这个,比如说,16个并行线程击中 同一分区 相同的记录 ,每次调用平均需要约600毫秒。如果将负载测试并行线程计数增加到24或30,则每次调用大约需要1秒钟。 我的问题是,一个服务结构可靠的字典能处理并行的“读取”操作吗,就像SQL Server能够处理并行的并发读取一样,而不会影响吞吐量?

    2 回复  |  直到 6 年前
        1
  •  0
  •   Diego Mendes    6 年前

    如果你查一下 Reliable Dictionary CreateEnumerableAsync Method ,您可以看到它被设计为并发工作,所以并发性不是一个问题。

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

    问题是 同时 不代表 快速的

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

    1. 必须先获取集合的快照,然后才能开始处理它,否则在处理过程中您将无法写入它。
    2. 您必须浏览集合中的所有值,以查找要查找的项,并在返回任何内容之前记下这些值。
    3. 从磁盘加载数据如果还没有在内存中,只有键保存在内存中,值在不需要时保存在磁盘中,并且可能会被分页以释放内存。
    4. 以下查询可能(我不确定,但我假定)不会重用前一个查询,您的集合可能自上次查询后发生了更改。

    当有大量的查询以这种方式运行时,会出现许多因素:

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

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

    如果你真的想用这种方式,我建议你把它设计成 Index Table 在一个字典中存储按ID索引的数据,另一个字典中的键是搜索值,值是主字典的键。这会快得多。

        2
  •  0
  •   Oleg Karasik    6 年前

    根据我看到的代码,你所读到的都是在 初级的 副本-因此,您有7个节点和60个服务实例来处理请求。如果我一切都好的话 六十 处理请求的副本。

    你有 节点和 六十 副本——因此,如果我们设想它们在我们拥有的节点之间或多或少地均匀分布 每个节点的副本。

    我不确定每个节点的物理配置,但是如果我们假设每个节点有4个VCPU,那么您可以想象,当您在同一个节点上发出8个并发请求时,所有这些请求现在都应该使用4个VCPU执行。这种情况会导致工作线程 战斗 对于资源-保持简单,会显著降低处理速度。

    这里之所以能看到这种效果,是因为你 扫描 这个 IReliableDictionary 而不是使用键获取项目 TryGetValueAsync 就像它应该的那样。

    您可以尝试更改代码以使用 TryGetValueAsync 两者之间的差异将非常明显。