【问题标题】:Is there a design pattern for dealing with large datasets over the internet?是否有用于处理 Internet 上的大型数据集的设计模式?
【发布时间】:2009-11-03 21:45:27
【问题描述】:

我正在寻找一种通过 Internet 处理大型数据集并定期更新这些对象的设计模式。我正在开发一个应用程序,它将一次在 UI 中显示数千条记录。此外,这些对象的各种属性非常短暂,需要在客户端上更新,以使用户了解系统中这些记录的变化状态。我对如何解决这个问题有一些想法,但我认为可能有一个(或多个)设计模式可以处理这种类型的场景。

限制:

  1. 客户端是用 Silverlight 编写的。
  2. 对象本身并不是很大(大约 15 个值类型和字符串属性),但是查询所有数据的开销很大。大约 15 个属性包含来自各种来源的数据;没有聪明的连接语句或索引会加速查询。我正在考虑在初始加载时仅填充属性的子集,然后在用户放大给定的对象分组时填写更昂贵的细节。想想 Google 地图,但不是街道和建筑,而是显示对象。
  3. 我将能够限制正在更新的数千个对象的部分。但是,我将需要用户能够“缩小”一个允许精细更新到显示所有数千个对象的上下文。我想当对象离开足够的缩放上下文时,更新将再次被禁用。

关于如何解决全部或部分问题的想法?就像我提到的那样,我已经在考虑一些想法,但到目前为止,我所做的一切都没有让我对这个项目的成功有很好的感觉。

编辑:

我认为困难的部分实际上归结为两件事,我可能需要两种不同的模式/实践/策略:

  1. 通过 Internet 加载大量记录 (~5k)。
  2. 通过 Internet 保持这些对象的子集 (~500) 保持最新。

有几种设计模式可以用于其他一切。

编辑 2:

感谢 Silverlight 中各种“推送”实现的链接。我可以发誓套接字已从 Silverlight 中取出,但根据下面的答案找到了 Silverlight 3 参考。无论如何,这对我来说真的不是一个大问题,而且我没有花太多时间研究,所以我正在从原始文本中编辑它。无论更新是通过投票还是通过推送,一般的设计问题仍然存在。很高兴知道我有选择。

编辑 3:推送技术的跟进。

我怀疑 Silverlight WCF 双工实现是comet-like push。这不会扩展,并且有很多文章介绍了它在现实世界中是如何扩展的。

Silverlight 中的套接字实现存在多种缺陷。看起来它在我们的场景中将毫无用处,因为 Web 服务器可能位于不允许非标准端口的任何给定客户端防火墙后面,并且 Silverlight 套接字不会连接到 80、443 等。

我仍在考虑以某种有限的方式使用 WCFduplex 方法,但看起来轮询将成为答案。

编辑 4:找到解决一半问题的模式

我发现 this pattern (PDF) 说明了使用迭代器模式从服务器检索数据页面并将它们呈现为简单的迭代器。在 .Net 领域,我想这将被实现为 IEnumerable(示例代码使用 Java 和 Oracle SQL)。我特别感兴趣的是异步页面预取,基本上是在客户端缓冲结果集。对于 5k 个对象,所有内容都无法立即显示在屏幕上,因此我可以使用一种策略,即不立即获取所有内容,但从 UI 中隐藏实现细节。应用程序将检索的核心对象位于数据库中,然后需要其他查找来完全填充这些对象。这种方法似乎是一种将一些数据快速发送给客户端的好方法。

我现在正在考虑使用这种模式 + 某种代理对象模式来侦听结果集的增量并相应地更新对象。在这里可以采取几种策略。我可以预先加载所有数据,然后发送更改的增量(这可能需要子系统中的一些额外代码来提供更改通知)。这可能是我的第一种方法。我还在寻找。感谢到目前为止的所有想法。

【问题讨论】:

  • 非常有趣的问题!我一定会喜欢在某个时候从事这样的工作。我能问一下你正在建造什么的细节吗?
  • 它是一个我们尚未发布(显然)的专有系统,因此我混淆了一些细节。一般来说,它是一种监控管理大量“设备”(当前最大安装量约为 5k)的应用程序中正在发生的事情的方法。该工具将允许管理员从整体上查看“设备网络”并发现问题,然后在问题区域采取行动。抱歉,我无法更具体。当我们发布时,我可以给你看!

标签: c# wcf silverlight design-patterns silverlight-3.0


【解决方案1】:

proxy design pattern 是有助于将数据从一个点传输到另一个点的模式。代理设计模式将允许您将远程对象视为本地对象。

【讨论】:

  • 我认为这种通用模式可能有效,但我遇到的问题是更多细节。尽管如此,这让我想到了将所有对象缓存在服务器的某个地方并在那里更新它们,然后在客户端轮询时为客户端上的代理提供增量......
  • 更清楚地说,这种情况下的代理本质上是断开连接的,因此需要保持一些状态。我不确定用于更新或初始加载的最佳模式。我已经与同事讨论过这个问题,一个想法是使用存储库/工厂模式将对象提供给服务器,这些对象将像代理一样,使他们自己的数据无效并定期回调到存储库(以及服务器)循环。
【解决方案2】:

可以提出2个解决方案

1) 压缩您的收藏并在传输后解压缩。

2) 使用享元+代理模式。

【讨论】:

  • 我打错了 - 应用程序将加载大约 5000 (5k) 个对象,而不是 5000k 个对象。我已经将这么多对象加载到内存中,Silverlight 可以很好地处理它。我正在考虑使用某些版本的代理模式,但享元在这里并没有给我太多帮助。
  • 我确实最终使用了一种享元,通过使用 bean 而不是向 Silverlight 提供丰富的对象图。
【解决方案3】:

我想知道您是否可以首先减少进入客户端屏幕的数据量?无论如何,您无法同时查看 5,000 个数据点。如果您需要滚动查找重要内容,请考虑从一开始就过滤掉不重要的内容。考虑一些 UI 设计(仪表板和仪表类型的东西),以便用户只看到问题点。然后他们可以深入研究并根据需要采取行动。

我知道你不能透露细节,我做了很多假设,这不是对你的问题的直接技术答案 - 但也许重新考虑必要的数据馈送将有助于推动你朝着更有效的方向发展后端和前端。

【讨论】:

  • UI 需要是一种“谷歌地图”界面,所以我们肯定需要显示所有数据点。你说得很好,我正在考虑最后加载屏幕数据或延迟加载它。
【解决方案4】:

在这里我找到了一篇似乎解释了如何在 Silverlight 2 中创建套接字的文章 Silverlight 2 and System.Net.Sockets.Socket

我还没有很深入地阅读它(我这样做有点太晚了),但它似乎可以用于你的情况。我看到的主要限制是您的 silverlight 应用程序只能连接到下载它的服务器。

这里有第 9 频道的教程Silverlight using socket

我希望这会有所帮助

【讨论】:

  • 我对 Silverlight 中的套接字进行了大量研究。有一些严格的限制阻止我使用它们。一个限制是它们可以工作的端口范围非常小,这对我来说似乎很愚蠢。
【解决方案5】:

总的来说,我认为您的问题的答案是没有一种或多种设计模式可以真正解决您的问题。相反,这是一个体面的大规模应用程序的一个很好的例子,它只需要大量的规划和设计工作。

在您进行设计时,我想您会遇到一些可能对您有所帮助的 DP,但这个巨大的东西应该如何工作的细节更多的是一个普遍(且有趣)的设计问题。

也许稍微澄清您的问题可能有助于引导人们就该系统的整体设计提供建议。此外,一旦您投入一些设计/努力来提出如何工作的高级设计,您可以要求批评/建议。很难有人完全想出这个作为 StackOverflow 问题的答案。 :)

【讨论】:

    【解决方案6】:

    如果我理解正确的话,这里确实有两个问题:

    1. 系统状态由来自多个数据源的数据表示。结果查询状态是昂贵的。
    2. 描述系统状态的数据量很大。因此,查询所有描述状态的数据的成本很高。

    解决这些问题的标准模式是引入中间层并使用增量来更新状态。例如:

    1. 显然,您不希望 Silverlight 客户端直接与后端系统通信。这不仅不可能,而且效率也很低,因为每个客户端都可以向同一个数据源询问其状态。为避免这种标准解决方案是引入一个中间层,该中间层聚合来自所有后端数据源的数据,并为客户端提供通用接口。结果,后端数据源将仅根据需要进行轮询(可以在中间层中为每个数据源配置),并且客户端也不必处理那些支持的数据源的细节。此外,您可以根据客户端最常见的查询,在中间层实现数据索引。

    2. 假设每条记录都有 ID,客户端应该只请求自上次更新以来的增量。许多模式之一是使用时间戳。例如。当客户端初始化它请求系统的状态时,中间层发送该状态,带有时间戳。当客户端需要更新某些记录时,它会在请求中提供 ID,以及最后一次更新的时间戳。因此,中间层将仅发送自上次时间戳以来的更改,并且仅发送请求的 ID。如果 object 有 15 个属性,并且自上次时间戳以来仅更改了其中的 3 个,则 update 将仅包含这 3 个属性的值。

    对于推送与轮询——推送并不是最好的解决方案。这实际上归结为客户端需要更新的频率和客户端/中间层之间的流量之间的权衡问题。例如。如果状态更改频繁但稀疏(例如一次只影响几个属性),并且不需要立即更新客户端的状态,客户端可能更喜欢累积更改而不是接收每个更新,因此轮询会更可取。

    【讨论】:

    • 我同意跟踪中间层的增量对于这个应用程序的性能至关重要。我们确实有一个与您在第 1 点中描述的非常相似的中间层,但它大多是无状态的。
    【解决方案7】:

    我认为您可能遗漏了一些东西:从 Silverlight 3 开始,就有了将数据推送到客户端的能力。 Here 的文章可能对此有所帮助。

    【讨论】:

    • 我曾想过,但这并不是真正的推动(尽管它可能会起作用)并且并没有真正解决我的设计问题。服务器系统在查询子系统之前不知道更新已准备好,这意味着无论如何都要在服务器端进行轮询。我可能会在服务器上缓存结果以加快速度...感谢您的链接。
    • 跟进:我阅读了更多关于轮询双工课程的内容。看起来它确实存在缩放问题。
    【解决方案8】:

    我对几个好的答案投了赞成票,但提出了一个解决方案,对后端数据进行了一些更改,并提供了一种从 Silverlight 检索数据的新方法。以下是解决此问题的措施:

    1. 我正在使用 bean 来表示那个大数据图。这删除了很多传输 XML。无论如何,我只关心数据的一个子集,尽管它是一个相当重要的子集。通过将数据扁平化为 bean,我认为我已将序列化对象大小减少到原始对象图的 20-25% 左右。
    2. 现在后端几乎所有的数据都会有一个字段表示上次修改的时间。我能够为所有大数据得到这个。有一些数据不会有这个,但是查询性能和数据聚合的真正问题已经解决了。作为其他人的通用解决方案,它看起来在许多 DBMS 中实现起来相当简单。
    3. 我正在编写新的 API 来检索在提供的 DateTime 之后已更新的数据。这使我可以仅从后端系统查询新的和更改的对象(这是调用这些 API 的 Web 服务,而 Silverlight 正在调用 Web 服务)。
    4. 聚合 Web 服务中的更改并检测数据图的一部分是否已更改。为简单起见,如果有任何变化,我只会发送整个数据图。这实际上是最难弄清楚的部分。数据图的一部分可能有新的更新时间,但图的核心对象尚未更新。我最终不得不编写 API 来查找子对象的更改,然后 API 来根据这些子对象(如果它们已更改)来查找根对象。对象图可以与自上次轮询以来尚未更新的根对象(实际上是对象图的大部分)一起返回。 Web 服务逻辑正在查询少量更改,因此即使单独查询并不便宜,每次轮询它们也可能只运行几次。即使在我们产品的非常大的安装中,这个查询循环每个轮询周期也只会运行 10 或 20 次(请参阅下面的轮询解决方案)。虽然我们的系统非常动态,但在 30 秒内变化不大。处理所有这些的 Web 服务调用对初始加载调用的反应与轮询一样。它所关心的只是检索比给定时间更新的数据。
    5. 我编写了一个从 ObservableCollection 继承的集合,用于处理查询和轮询。使用此集合的客户端代码提供了一个查询数据的委托。日期以页的形式异步返回。我还没有确定页面大小。它不断地重新查询页面,直到服务器返回一个小于最大页面大小的页面。该集合还提供了有关如何确定集合中最新对象的最新日期的信息。它会定期轮询比集合中最新项目更新的更新。实际上,这个“最新日期”实际上是一个包含原始对象图各个部分的多个日期的对象。如果从服务器返回的项目存在于集合中,则集合中的项目将使用返回的数据进行更新。我这样做而不是插入新项目并删除旧项目,因为它适用于更多数据绑定情况。

    这种模式可以改进。我只能将增量发送到 Silverlight 以进行更改。我仍然可以尝试使用某种推送技术。但是这个解决方案给了我一个 Web 服务调用,它可以返回各种情况下的数据。轮询也非常简单,只需一件事即可完成所有数据检索。没有很多活动部件。这通过相同的机制在初始数据加载期间和轮询期间处理对象状态更改。这似乎也可以很好地扩展。最初的调用似乎是最昂贵的,随后的调用运行得越来越快。我认为这是因为保留在后端的数据随着每次传递而变得越来越小。

    我还有一个关于我的实现的问题I have posted here

    感谢所有建议。虽然我没有听从所有的建议,但有几个想法直接帮助了我,或者让我想到了一条不同的道路来解决这个问题。

    【讨论】:

    • 你能澄清/解释你所说的“豆子”是什么意思吗?
    • Bean 是用于 UI 消费的持久类的常用术语。标准业务/模型类对于 UI 的使用可能很麻烦,因此 bean 填充了它们的值并由 UI 使用。在我的例子中,服务器上的业务层有各种各样的模型对象,它们有一些相当丰富的对象图。我将这些转换为用于 Silverlight 消费的 bean。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多