我有一个客户端-服务器应用程序,它从几个表中获取所有数据,重新计算并存储它。
例子:
每个项目都有一个“材料清单”= 其他项目的清单和数量。因此,一个项目的成本是其 BOM 中项目的成本 * 它们的数量之和。最终,一些“基础”项目没有 BOM,只是独立设置成本。(即:原材料)
即:A 的 BOM 说它由 2xB 和 3xC 制成。
我现在所做的,我不记得我为什么这样做,是我从数据库中获取所有项目和所有 BOM,并一次针对每个项目递归计算其成本。一旦我计算了一个项目,我就会标记它,这样我就不会再重做成本了。(也防止无限递归)
事情是,这有点愚蠢:首先,它很慢并且会重新计算没有改变的东西,更糟糕的是,给它一个足够大的数据库,它会耗尽内存。
相反,我可以按需重新计算项目:当一个项目的 BOM 发生变化时,我重新计算该 BOM,然后选择包含此更新项目的所有 BOM,并重新计算它们;冲洗并递归重复,直到到达顶部,数据库中没有 BOM 依赖于任何更改的项目。
这在实践中意味着什么:假设一些项目是原材料,其成本可能会经常更新,而一些项目是“最终用户”的东西,它们的 BOM 很少会改变。当用户更改其中一种材料的成本时,可能意味着要处理数千个项目,重新计算它们。假设 1 个项目/BOM 的 SELECT 需要 15 毫秒(我在 Postgresql 上),然后仅 SELECTing 1000 个项目/BOM 将需要 15 秒,然后您必须将重新计算的成本更新回数据库中的项目......哦亲爱的,延迟现在可以变成几分钟。
我工作的公司使用的 ERP 软件采用第一种方法:一次批量重新计算整个数据库。从字面上看,这需要几个小时,而且在 10 多年的使用中,这种方法似乎已经出现了问题。批量重新计算每周进行一次。
既然我实际上已经“大声写出来”,我认为几分钟的时间并不重要。问题是我不太了解数据库,而且我担心并发性:由于在项目 A 上更新需要很长时间,因此很可能有人会在项目 A 正在更新期间更新第二个项目 B更新。
假设 D 项是由上面的 A 和 B 组成的。用户 1 更新 A,因此服务器软件开始与数据库自慰几分钟,最终更新 D。但与此同时,用户 2 更新 B,因此服务器最终将再次更新 D。
使用 Postgresql 的事务会解决问题吗?事务从数据库的当前状态开始,因此事务 1 看到 D 由 A1 和 B1 组成,并将 A 从 A1 更新到 A2,但在它完成并提交之前,事务 2 将开始,也看到 A1和 B1。T1 重新计算并提交,D = A2 + B1。但是T2已经开始了,并没有看到新的A,A2。因此,它最终将 D = A1 + B2 提交给 DB,这是不正确的。它应该是 D = A2 + B2。
此外,一些处理会重叠,浪费服务器时间。
如果我按顺序而不是并行执行 T1 和 T2,那么万岁,答案是正确的,但用户 2 将不得不等待更长时间。此外,如果一组事务彼此没有关系(完全独立的......依赖树;即:A=X+Y 和 B=N+M),那么并行计算将给出正确的答案并且对于用户。
重要提示:即使按顺序处理,我仍然会使用事务,因此软件的其余部分仍然可以并行处理该数据,除了重新计算成本的功能。
现在,如果……数据库延迟不会那么“糟糕”,那么整个“按顺序处理”的事情就不会那么糟糕了。比如说,如果整个数据都保存在 RAM 中,那么处理 1000 个对象将是轻而易举的事。啊,但是即使我构建了一个系统来快速将大块数据移入/移出磁盘/RAM并进行一些缓存 - 以替换 DB - ,那也行不通,因为我仍然需要事务,以便服务器的其余功能可以并行工作。(上面的“重要说明”)所以我最终会建立另一个数据库。可能会快一点,但它愚蠢/浪费时间。
我“缓存”每个项目的成本的全部原因是我每次使用它时都不会重新计算它,因为它不仅浪费了有限的资源,而且数据库延迟太大而且并发问题的规模更大。
现在我不需要奇怪为什么“他们”会大批量这样做……这让我很头疼。
Q1:你们如何以“最佳”方式解决这个问题?
根据我目前的理解(即在遇到之前我默默忽略的并发问题之后),我会让那个函数按顺序使用事务,而应用程序的其余部分仍然可以并行使用数据,我相信最适合用户。这就是目标:对用户最好,但保证系统的正确性。
也许稍后我可以向它扔硬件并使用软件黑魔法来减少延迟,但我现在开始对自己撒谎。
另外,在过去的几个月里,我对一些显而易见的事情完全视而不见(有些与编程无关),所以我期待有人会指出一些我设法错过的可耻的明显事情......: |