写入密集型特性的架构设计

8
我目前的项目使用Ruby on Rails作为后端,Oracle数据库和memcached。其中有一个非常频繁使用的功能,依赖于单个数据库视图作为数据源,这个数据源内部包含其他的数据库视图和表。它是一个虚拟的数据库视图,可以从一个地方访问所有内容,而不是一个物化的数据库视图。用户大多数时候都在查看他们想要更新的功能,因此保持数据最新是很重要的。当从这个视图获取数据时,我会将安全表与视图进行内部连接(安全表本身不是视图的一部分),其中包含了一些字段,我们用来控制更细粒度的数据访问。例如,安全表具有user_id、prop_1、prop_2列,其中prop_1、prop_2是数据库视图上可用的列,而user_id是已登录的用户。一些用户在安全表中具有相同的属性,比如prop_1 = 1 and prop_2 = 1,但也可能像其他用户一样具有相同的prop_1,但具有不同的prop_2,比如prop_1 = 2 and prop_2 = 1。prop_1和prop_2有许多不同的组合,可以将它们视为另一个表的外键,因此可能有许多条目。现在检索应用程序中的记录的时间几乎是10秒,非常缓慢。我正在考虑替代方法。我首先想到的是物化视图,但由于用户经常进行更新,这可能不是最好的选择,因为刷新视图可能需要时间。我想到的第二件事是使用缓存,将prop_1prop_2组合作为底层数据的复合键,因为许多用户具有相同的组合,谁具有相同的组合就可以访问相同的数据。然而,这种方法可能需要更多的代码重写和逻辑来保存和检索数据片段,而不是像在数据库视图中那样从一个位置进行一次查询。在您的经验中,您是如何解决相同/类似问题的?或者是否有更好的方法我可以尝试?

1
你的问题似乎暗示了与安全表联接是导致性能缓慢的原因。如果没有这个联接,检索类似记录需要多长时间? - Hoàng Long
4个回答

1

如果没有更多关于您观点的信息,很难给出一个好的答案,但我会尝试一下。

首先,我质疑使用单个非常复杂的视图。这很难调整,通常会导致性能问题,因此,如果可能在应用程序中将其拆分,那么这将是我的第一选择。

其次,您是否查看了包含安全过滤器的查询的执行计划(解释计划)?它是否使用合理的索引?如果没有,请创建它们。例如,安全属性可能没有索引?

第三种选择可能是使用PL / SQL,并调用充当视图的存储过程。这使您可以在数据库中获得更多控制,从而使其可以控制查询并将其拆分为多个步骤,但以获得与今天相同的结果。

最后,您可以重写视图以获得更好的性能。经常被忽视的一个功能是WITH子句,它使得在主查询之前运行查询并将结果用作表成为可能。它帮助我极大地提高了复杂视图的性能。

DBMS_RLS很酷,但可能很昂贵,它需要企业版,而且我不会感到惊讶,如果您还需要单独的许可证。我会首先选择编程解决方案。


1
如果您遇到数据库延迟的问题,您可以将一些视图迁移到REDIS数据库(内存数据结构存储),这可能是最有效的“读/写”密集型之一。
关于更新问题,您可以实现websocket,直接向需要更新的人传播/推送精确的更新。
我强调,这种可能性需要在客户端和服务器端进行一些修改,但我认为这是保持最终用户视图更新且延迟较低的最佳方法。
此致敬礼。

0
“依赖于单个数据库视图作为数据源,而此数据源内部包含其他数据库视图和表。”
如果这是一个对象,我们会称之为God Object,这是一件坏事。在数据库领域中同样如此。如果不知道细节,很难确定,但可能存在内部连接、外部连接和交叉连接的混乱,导致去规范化、数据重复和(也许)完整性问题。
肯定会有性能问题,因为这种情况是无法调整的。无论您想要一行还是一万行,都是相同的查询。您没有给优化器做出明智决策的机会。
因此,您需要做的第一件事是将此视图拆分为映射到专注业务领域的有意义的数据对象(视图或表)。您已经在使用Rails,管理更好的数据访问层应该不难。

关于安全性,Oracle确实有内置的虚拟私有数据库实现。如果您使用企业版,则应该绝对使用DBMS_RLS来控制行级(和列级)访问权限。 RLS的主要优势在于它是不可见的:在表或视图上设置策略,它会自动应用于对象上执行的所有SQL。

如果您使用标准版,则只能使用显式加入到安全表中(但请参见下文)。

至于使用memcached,根据我的经验,应用程序开发人员倾向于构建外部缓存,因为他们不了解Oracle数据库的工作原理,因此实现了糟糕的数据访问策略 - 例如将所有数据路由通过单个庞大的视图...

将您的数据访问层(DAL)分解成有意义的离散对象,可以提高性能,因为数据库优化器将能够选择提取所需精确信息的最有效路径。此外,检索路径也会更好,因为热块(最常查询的块)将在数据库缓冲区缓存中得到帮助,而目前我怀疑这些块正在被过多的全表扫描完全破坏。您可以利用服务器结果缓存,这可能有助于“用户具有相同组合并且可以访问相同数据”的情况。了解更多
因此,您可能会发现根本不需要外部缓存。通过适当使用技术,让数据库正确管理其数据,您应该会发现需要在外部保存的数据量大大减少。您将应用程序描述为“写入密集型”,因此您必须花费大量时间来保持缓存和数据库同步。显然,如果您处理Facebook数量的数据,您需要使用Facebook风格的数据管理方法。但通常,做最简单的事情可能是最好的起点

0

很多时候加入复杂视图会导致性能问题。

prop_1prop_2是你想限制的值吗?也就是说,你是在这些列上将你的视图与安全表连接起来吗?

WHERE  my_view.prop_1 = security_table.prop_1
AND    my_view.prop_2 = security_table.prop_2
AND    security_table.user_id = :current_user_id

?

下一个问题: prop_1prop_2 是否映射到视图底层表中的列?如果是,它们可以用于快速访问底层表中的行(在视图之外)吗?
如果是这样,我建议使用DBMS_RLS.ADD_POLICY在底层表上添加安全策略来执行您的安全性(即基于当前用户限制prop_1prop_2的值),并且根本不将安全表连接到视图。
如果您将安全策略添加到底层表中,则Oracle将在访问表时添加这些谓词,在查询复杂性开始之前。这可能会为Oracle的优化器提供额外的帮助,使过程更快。
没有看到您的代码,很难再说更多了。

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