2011-02-28 58 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