2011-02-28 60 views
2

我有一个查询需要很长时间,我想优化它。我正在寻找最有效的方式来做到这一点。优化查询 - 使用字段或使用其他表格

我正在使用PostgreSQL数据库的Hibernate/JPA,但任何解决方案应该是一个通用的JPA之一。

术语

  • 用户:在系统中的用户。
  • 朋友:用户的一个朋友。用户将有N个朋友。
  • 会议:使用系统的会话。可以打开或关闭。
  • 上下文:会话的上下文。用户可能在任何给定时间内每个上下文有一个打开的会话,并且每个上下文可能有许多过去的关闭会话。

查询

我需要执行一个查询,给定一个用户名,使我有以下几点:

  • 获取该用户的所有朋友
  • 对于每个朋友:
    • 如果朋友有任何打开的会话,请获取所有打开的会话(针对所有上下文)
    • 否则,请将朋友的最新会话排除在所有上下文之外。

注意,友谊都是存储在不同的数据库,所以我不能将此主题融入在任何情况下,一个大的查询。

用户A具有三个朋友:B,C,d。有两个上下文,1和2。朋友具有以下数据:

(下面的格式是会话ID - 用户,语境)

  • 1 - B,1:打开会话
  • 2 - B,2:开始关闭会话年02月 - 27
  • 3 - B,2:上开始闭会话FEB-26
  • 4 - C,1:02月27
  • 5开始关闭会话 - C,1:关闭会话2月26日开始
  • 6 - C,2:02月 - 26
  • 7开始关闭会话 - C,2:关闭会话年02月 - 25
  • 8开始 - d,1:打开会话
  • 9 - d, 2:打开会话

查询应得到我: B:第一节(所有打开的会话) C:第4节(最新的非公开会议) d:8,9会话(所有打开的会话)

当前状态

我查询的工作分三个步骤:

  1. 获取用户
  2. 的各界朋友对每个朋友:
    1. 获取所有打开的会话的朋友
    2. 如果有任何公开会议,返回所有公开会话
    3. 获取最新的会话为朋友,返回该会话

显然这是很多的疑问。 对于初学者,我将采取上述步骤2并将其转换为单个查询。我的担心与第二个查询有关。问题是 - 如何使其更加优化。因此,该问题可以改述:

“给定一组N个朋友ID,获取所有这些朋友的所有开放会话或最新会话。”

建议的解决方案

有我们想出了两种基本的解决方案,我们正在考虑这将是更好的。

表解决方案表示将保留一个将在用户,上下文和最新会话之间关联的新表。该解决方案的含义是:

  • 创建一个新的实体&表“最新会议”
  • 表将具有这些列:
    • 用户
    • 语境
    • 最新会话ID
  • 该表将由会话实体在post persist上进行更新,以便任何新的p ersisted会话将自动更新此表。
  • 新查询将从此表中获取用户所有朋友的所有记录,并对其进行处理以创建最终结果。

列解决方案表示在会话表上保留“最新”标志列。该解决方案的含义是:

  • 创建最新的(一个布尔值)一个新的领域
  • 列将通过邮寄方式设置持久会话实体,使昔日的“最新”会议将不再是最新的,新的会议将成为最新的会议。
  • 新查询将从原始会话表中获取用户所有朋友的所有最新记录(通过将新列纳入语句的条件中)并对其进行处理以创建最终结果。

这些都有优点和缺点,我们似乎还没有赢家。显然,可能还有其他更好的解决方案我们没有考虑过。我希望看到的是以上哪个更好,为什么,或者是你自己的更好的方法。

+0

为什么不同的数据库友谊?它是真的不同的数据库或不同的架构? – Unreason 2011-02-28 10:42:43

+0

如何使用功能视图? 缓存可能是一个很大的帮助,如果可能的话。 – 2011-02-28 10:16:18

+0

据我了解,一个视图会以这种或那种方式运行复杂的查询,并且我试图通过在数据库更新期间引发更多管理来避免这种情况,从而提高查询的性能。 – 2011-02-28 10:28:28

回答

1

您的两个解决方案之间的差异应该是微乎其微的。根据活动情况,表格解决方案可能更清洁

然而,请注意'你做错了'(根据理论)。

RDBMS应用程序设计原则明确指出,您不应该试图指定应该如何执行查询,而是指定您想要的数据。数据库将找到解决方案的最佳路径(RDBMS距离数据最近,取决于您的架构可能会节省网络往返次数,存储往返等等;可扩展性可能会严重受损,您可能不会意识到这一点您不会进行体面的压力测试;此外RDBMS知道索引和内部统计信息,这些信息可确定扫描或搜索是否更有效,并知道如何以最佳方式执行联接)。

在实践中,尝试提出为什么不同数据库友谊的问题? (它是真的不同的数据库或不同的架构在同一个数据库?)。

此外,如果你真的想要去的方式,你这样做(禁用RDBMS寻找最优的执行计划),那么最重要的因素是:

  • 指数(会影响订单的性能幅度
  • 使用模式)(指标将提高SELECT查询的性能,但过多的索引会减慢更新)
  • 应用/客户层高速缓存(可以影响量级)
性能和可扩展性

编辑: 因此,考虑到“给定一组N个朋友ID,获取所有这些朋友的所有公开会议或最新会议。”这里是一个应该引入新的结构

会话(会话ID,用户,上下文,开始,结束)

SELECT * 
FROM Sessions s 
WHERE s.End IS NULL 
     AND s.User IN (:friendsList) 
UNION ALL 
SELECT * 
FROM Sessions s 
WHERE s.User NOT IN (SELECT User 
        FROM Sessions s2 
        WHERE s2.User IN (:friendsList) 
          AND s2.End IS NULL) 
     AND s.User IN (:friendsList)   
     AND s.End IN (SELECT MAX(End) 
        FROM Sessions s2 
        WHERE s2.User = s.User) 

有更多的方法来写上面,试图帮助优化前进行测试的查询,特别是如果您的数据库支持CTE,则上述内容可以更有效地重写。

备注: :friendsList - 作为好友的用户列表。
此外,我假设开放会话的开放会话的值为End。您可能已经选择了其他方法(也许您有一个字段表示它;或者有两个表,一个用于打开会话,一个用于关闭)

上述查询将受益于某些索引(原则是先尝试优化索引,然后进行重组;我会尝试的第一个索引是User, End上的复合索引)以及相对较少的朋友(假设它以字符串形式传递的事实假设),这应该表现得很好。

+0

谢谢。正如你所看到的,我没有试图优化给定的查询 - 我知道查询很复杂,所以我试图向数据库添加更多信息以简化它。如果你愿意,我将数据添加到数据库以简化最终的查询逻辑。根据理论,这是错误的吗? – 2011-02-28 10:51:33

+0

@Eldad Mor,是的,根据良好的设计原则,这是错误的。原因a)你已经将本来应该是一个查询的内容分成了三个b),以改善你开始构建缓存结构的三个查询的糟糕表现。实际上,这实际上有时是显着提高绩效的唯一途径;然而,因为你并没有从一个单一的查询开始,那么这是一个过早优化的例子,你可能正在为一个不存在的问题(实际上是存在的,但是是自制的)开发解决方案。 – Unreason 2011-02-28 10:56:23

+0

好的,我同意你的观点,尽管它是2个查询而不是3个。我基本上认为,在给定现有数据库模式的情况下创建第二个查询在SQL和冗长的性能方面将会非常复杂。我同意避免过早优化,但这是迄今为止最复杂的查询 - 我相信通过向数据库添加一些数据,我将大大简化它。 – 2011-02-28 11:33:28

0

为什么不缓存对象?你不需要打数据库。

+0

我正在使用缓存,但是这个查询并不常见。用户会偶尔使用它,但通常不足以允许缓存真正提高性能。 – 2011-02-28 10:29:33

+0

虽然这个查询不是一个常见的,因为你说对象已经在缓存中,那么为什么不使用它呢?如果以下对象位于缓存中 - 用户,朋友(用户),会话,则查找其简单对象。但是,使用已列出的数据库选项 – isobar 2011-03-01 03:21:12

+0

1.创建新表会在会话保存中添加延迟,并且您还需要在会话过期时将其消除。它是一个开销。是的,选择会更快。它还引入了一点数据冗余。如果会话表有大量的记录,那么这种方法可能会更好。 2.作为更新中的另一列,没有太多的额外开销。但是,如果会话表有大量数据,则查询速度会变慢。 – isobar 2011-03-01 03:21:42

0

您的主要瓶颈似乎是,您需要的信息分布在两个数据库的事实。因此,您可以获取朋友列表并通过它们进行传播。

我建议您尝试删除迭代,将其减少为单个查询。

我会实现这一目标的方式是建立逗号分隔的用户标识字符串,并将该字符串传递给第二个数据库。然后,第二个数据库中的sql可以(例如使用函数)将字符串intol转换为单个ID字段表,然后加入。

它对我来说非常不雅,但这是我一直都在做的事情。

我已经使用的唯一实用的替代方法是构建一个将ID插入到表中的单个查询,然后加入到该表中。无论是临时表还是具有SessionID字段的永久表,都允许多个会话同时使用它。

无论使用什么方法,对第2步使用单个查询,使用基于集合的方法而不是迭代,应该会产生显着的好处。

+0

也许我不清楚:-)我不想遍历朋友并查询每个朋友。我打算在完整的朋友列表上运行一个查询。我无法合并这两个DB,这是给定的,但我可以将整个过程转换为两个查询 - 一个用于提取朋友,另一个用于提取会话。这是我在这里关注的第二个查询。 – 2011-02-28 10:54:10