在应用程序中推送数据更改 vs 拉取数据更改

4
假设您有一个应用程序,由两个层组成:
A:数据层,存储从数据库或文件加载的所有数据
B:显示漂亮用户界面中的数据,例如图形报表的层
现在,在A层更改了数据。我们有两种方法来确保从B层生成的报告被正确更新。
第一种方法是PUSH方法。A层通过观察者通知B层,以便B层可以更新其报告。
PUSH方法存在几个缺点:
如果数据多次更改(例如在加载期间或更改大量数据的算法中),则观察者会执行多次。这可以通过引入某种缓冲区解决(在更改时防止调用观察者),但这可能非常棘手,并且经常忘记进行正确的缓冲调用。
如果更改了大量数据,则观察者调用可能会导致应用程序无法接受的开销。
另一种方法是PULL方法。A层只记住哪些数据已更改,并且不发送通知(标记A层为脏)。在用户执行操作之后(可以运行算法或加载文件或其他内容),我们检查所有用户界面组件,并要求它们更新自己。在这种情况下,将要求B层更新自己。首先,它将检查其底层层(A层)是否脏。如果是,它将获取更改并更新自己。如果A层不脏,则报告知道它没有任何事情要做。
最佳解决方案取决于具体情况。在我的情况下,PUSH方法似乎更好。
如果我们有超过2个层,则情况变得更加困难。假设我们有以下4个层:
A:数据层,存储从数据库或文件加载的所有数据
B:使用数据层(A层)的层,例如使用复杂的过滤功能过滤A中的数据
C:使用层B的层,例如将B层的数据聚合成较小的信息块
D:解释层C的结果,并以漂亮的图形方式呈现给用户的报告
在这种情况下,PUSH更改几乎肯定会引入更高的开销。
另一方面,PULL更改需要:
层D必须调用层C以询问其是否脏
层C必须调用层B以询问其是否脏
层B必须调用层A以询问其是否脏
如果没有任何改变,那么在您知道实际上没有任何改变且无需做任何事情之前,需要执行的调用次数相当多。似乎我们试图通过不使用PUSH来避免的性能开销,现在因为需要多次调用以询问是否存在更改而在PULL方法中回来了。
是否有解决这种问题的模式可以以一种漂亮和高性能(低开销)的方式进行?
2个回答

3
没有免费的午餐,没有银弹。一切都取决于仔细的设计。您已经涵盖了常见技术,但需要小心地应用它们,并避免假设。
我对您的两个陈述提出质疑:
您暗示控制PUSH通知过于困难。在许多情况下,您往往会有一个主计算引擎,该引擎获取数据并进行计算。引擎必须在某个时候停止,在此时它可以发送“新数据准备就绪”事件,该事件可以包含有关更改内容的更精细信息。
您说进行4次跨层调用太昂贵了。这是基于什么?与什么相比?如果您担心乘数因子(10个D实例)调用(5个C实例)调用(2个B实例)调用(1个A实例),那么A会受到100个调用的打击,那么我们肯定要优化吧?每个级别都可以说:“如果我当前正在调用或最近听到答案,则无需再次调用”。
当考虑到层的扩展好处时,少量廉价查询可能不过度。

我不只有几个实例,而是有数百万个实例。但你的回答让我意识到了一些重要的事情。推送更改通常在实例级别上执行(每个实例更改都可能向前推动更改),而拉取更改则在层级别上执行(因为每个层只需拉取一次,无论实例数量如何)。这意味着在我的情况下,拉取可能比推送快得多。感谢您的提示。 - Patrick
我所指的实例是报告过程和Ds,而不是数据项的数量。你没有一百万个D吧?如果是这样,那我很佩服。 - djna

0

通过数据管理器推送,并压缩在小于n纳秒内发生的更改。 数据管理器实现发布-订阅。

这意味着数据生产者仅依赖于数据管理器,而数据消费者仅获取数据。

(对于消费者来说,存在依赖关系的反转。)

这使得所有数据流管道都明确地出现在您的粘合代码中。 订阅可以提前设置,因此消费者不需要知道其工作原理。

数据管理器可以使用自己的线程调用订阅者通知,这将生产者与消费者完美地解耦。 您可以轻松压缩更改,因为数据管理器仅使用1个线程进行通知,它可以通过计时器“通知”,当它醒来时,它只看到最新状态。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接