我有一个需要很长时间的查询,我想优化它。我正在寻找最有效的方法。
我正在使用Postgresql DB处理Hibernate / JPA,但任何解决方案都应该是一个通用的JPA解决方案。
术语
查询
我需要实现一个查询,给定一个用户名,给我以下内容:
请注意,友情存储在不同的数据库中,因此无论如何我都无法将其合并到一个大查询中。
示例
用户A有三个朋友:B,C,D。有两种情况,1和2.朋友们有以下数据:
(下面的格式是会话ID - 用户,上下文)
查询应该让我: B:第1节(所有公开会议) C:第4场会议(最新闭幕会议) D:会议8,9(所有公开会议)
当前状态
我的查询分三步进行:
显然这是很多疑问。 对于初学者,我将采取上面的第2步并将其转换为单个查询。我的担忧与第二个查询有关。问题是 - 如何使其更加优化。因此可以改写这个问题:
“给定一组N个朋友ID,获取所有这些朋友的所有开放会话或最新会话。”
建议的解决方案
基本上我们提出了两种解决方案,我们正在考虑哪种方法会更好。
表解决方案说要保留一个新表,该表将关联用户,上下文和最新会话。这个解决方案的含义是:
列解决方案表示在会话表上保留“最新”标志列。这个解决方案的含义是:
这些都有利弊,我们似乎还没有赢家。显然,我们还没有考虑过其他更好的解决方案。我想看到的是上面哪一个更好,为什么,或者你自己的更好的方法。
答案 0 :(得分:1)
两种解决方案之间的差异应该是微不足道的。根据活动,表解决方案可能更清洁。
然而,请注意'你做错了'(根据理论)。
RDBMS应用程序设计原则明确指出,您不应该尝试指定查询的执行方式,而应该指定您想要的数据。数据库将找到解决方案的最佳路径(RDBMS最接近数据,并且根据您的体系结构可能会节省网络往返,存储往返等等;可伸缩性可能会在这里严重削弱,如果可能,您可能不会意识到这一点你不进行适当的压力测试;此外,RDBMS知道索引和内部统计数据,它们确定扫描或搜索是否更有效,并且知道如何最佳地执行连接。)
在实践中,尝试提出为什么不同的数据库为友谊提出问题? (它在同一个数据库中是不同的db或不同的模式?)。
此外,如果你真的想采用这种方式(禁止RDBMS寻找最佳执行计划),那么最重要的因素是:
编辑: 因此,考虑“给定一组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
- 朋友用户列表
另外,我假设开放会话的NULL为开放会话的End
值。您可能已经选择了其他方法(可能您有一个表示它的字段;或者有两个表,一个用于打开会话,一个用于关闭)
以上查询将受益于某些索引(原则是首先尝试使用索引进行优化,然后进行重组;我会尝试的第一个索引是User, End
上的复合索引)和相对较少数量的朋友(假设从它作为一个字符串传递的事实来看,这应该已经很好地执行了。
答案 1 :(得分:0)
为什么不缓存对象?您无需点击数据库。
答案 2 :(得分:0)
您的主要瓶颈似乎是您需要的信息分布在两个数据库中。因此,您获取了一个朋友列表并通过它们进行了迭代。
我建议您尝试删除itteration,将其减少为单个查询。
我实现此目的的方法是构建一个以逗号分隔的用户ID字符串,并将该字符串传递给第二个数据库。然后,第二个数据库中的sql(例如,使用函数)将字符串intol转换为单个字段的id,并加入其中。
对我来说这感觉非常不合理,但这是我一直在做的事情。
我使用的唯一实用替代方法是构建一个将ID插入表中的单个查询,然后加入该表。临时表或具有SessionID字段的永久表,允许多个会话同时使用它。
无论您使用何种方法,对步骤2进行单一查询,使用基于集合的方法而非迭代,都应产生显着的效益。