2014-10-02 27 views
4

當前的實現是一個包含多個連接和臨時表的複雜查詢,但是對我的MySQL造成了太多的壓力,並且需要超過30多秒才能加載桌子。數據由PHP通過JavaScript Ajax調用檢索並顯示在網頁上。以下是涉及的表格:在分頁表中顯示大量數據而不會嚴重影響數據庫

Table: table_companies 
Columns: company_id, ... 

Table: table_manufacture_line 
Columns: line_id, line_name, ... 

Table: table_product_stereo 
Columns: product_id, line_id, company_id, assembly_datetime, serial_number, ... 

Table: table_product_television 
Columns: product_id, line_id, company_id, assembly_datetime, serial_number, warranty_expiry, ... 

單個公司可以在兩個產品表之間拆分100k個項目。產品表使用line_name進行聯合和過濾,然後按assembly_datetime進行排序,並根據分頁進行限制。日期時間值也依賴於時區,並將其作爲查詢的一部分(另一個JOIN +臨時表)應用。 line_name也是返回的列之一。

我正在考慮從產品聯合查詢拆分line_name過濾器。基本上我會確定對應於過濾器的行的ID,然後使用WHERE條件WHERE line_id IN (<results from previous query>)進行UNION查詢。這將會削減對連接和臨時表的需求,並且我可以將line_name應用於line_id和PHP中的時區修改,但我不確定這是處理事情的最佳方式。

我也研究過使用Redis的可能性,但大量的單個產品在通過PHP(20-30秒)將所有數據推送到Redis時導致類似的漫長等待時間,即使它只是直接從產品表中拉入。

  • 是否可以調整現有的查詢來提高效率?
  • 我可以推動一些處理到PHP來減少SQL服務器上的負載嗎? Redis呢?
  • 有沒有辦法更好地構建表格?
  • 您會建議哪些其他解決方案?

我很感激您可以提供的任何輸入。

編輯:

現有的查詢:

SELECT line_name,CONVERT_TZ(datetime,'UTC',timezone) datetime,... FROM (SELECT line_name,datetime,... FROM ((SELECT line_id,assembly_datetime datetime,... FROM table_product_stereos WHERE company_id=#) UNION (SELECT line_id,assembly_datetime datetime,... FROM table_product_televisions WHERE company_id=#)) AS union_products INNER JOIN table_manufacture_line USING (line_id)) AS products INNER JOIN (SELECT timezone FROM table_companies WHERE company_id=#) AS tz ORDER BY datetime DESC LIMIT 0,100 

這被格式化了一些可讀性。

SELECT line_name,CONVERT_TZ(datetime,'UTC',tz.timezone) datetime,... 
    FROM (SELECT line_name,datetime,... 
      FROM (SELECT line_id,assembly_datetime datetime,... 
        FROM table_product_stereos WHERE company_id=# 

       UNION 
       SELECT line_id,assembly_datetime datetime,... 
        FROM table_product_televisions 
       WHERE company_id=# 
       ) AS union_products 
     INNER JOIN table_manufacture_line USING (line_id) 
     ) AS products 
INNER JOIN (SELECT timezone 
      FROM table_companies 
      WHERE company_id=# 
      ) AS tz 
ORDER BY datetime DESC LIMIT 0,100 

ID被索引;主鍵是每列的第一個鍵。

+1

請顯示您的查詢。 – 2014-10-02 21:05:53

+0

並請提供有關主鍵和索引的信息。 – TheWolf 2014-10-02 21:09:12

+1

我並不完全確定,但我認爲您的查詢過於複雜。你的查詢應該返回什麼?另外,您可否請格式化查詢以使其更易讀? – TheWolf 2014-10-02 21:36:28

回答

0

PHP並不是一個解決方案...... Redis可以是一個解決方案。

但我要改變的主要是索引表的創建(添加缺少的索引)......如果你正在運行臨時表,你並沒有很好地爲表創建索引。而100k行幾乎沒有。

但我不能幫助你沒有任何表創建語句以及您運行的查詢。

確保您的「哪個部分」是您從左到右的b樹索引的一部分。

1

記住:索引快,數據慢。使用連接嵌套查詢。嵌套查詢返回所有數據字段,而連接只考慮過濾器(應該全部都被索引 - 確保table_product _ *。line_id上​​有唯一索引)。已經有一段時間了,但我很肯定你可以加入「ON company_id =#」,這應該儘早減少結果。

在這種情況下,所有結果都是指同一家公司(或者一個小得多的子集),因此分別運行該查詢(並使查詢更易維護)是有意義的。

所以你的數據來源是:

(table_product_stereos as prod 
INNER JOIN table_manufacture_line AS ml ON prod.line_id = ml.line_id and prod.company_id=# 
UNION 
table_product_televisions as prod 
INNER JOIN table_manufacture_line as ml on prod.line_id = ml.line_id and prod.company_id=#) 

從中你可以選擇督促。或毫升。根據需要填寫。

+0

我也需要處理過濾和分頁。查詢結束時,分頁只是ORDER BY和LIMIT。使用LIKE與輸入的字符串進行比較,在line_name上進行過濾。你會把這個放在哪裏? – DemonGyro 2014-10-03 13:13:42

+0

想想你的部分查詢。源代碼如下: 從{source} {filter}中選擇{fields} 在這種情況下,源代碼是括號內的表達式,在括號外可以添加額外的條件和順序。 – Mike 2014-10-03 21:11:38

2

讓我們從它的組件部分構建這個查詢,看看我們可以優化什麼。

觀察:您正在從兩個大型產品表的聯合中提取100個最近的行。

因此,我們首先嚐試優化從產品表中獲取內容的子查詢。這是其中之一。

   SELECT line_id,assembly_datetime datetime,... 
       FROM table_product_stereos 
       WHERE company_id=# 

但是你看,你只需要100個最新的條目在這裏。因此,讓我們添加

   ORDER BY assembly_datetime DESC 
       LIMIT 100 

此查詢。另外,你應該在這張表上放置一個複合索引,如下所示。這將允許索引滿足WHERE和ORDER BY查找。

CREATE INDEX id_date ON table_product_stereos (company_id, assembly_datetime) 

table_product_televisions開始的所有查詢都適用於此查詢。按照時間排序,將其限制爲100,並對其進行索引。

如果您需要應用其他選擇標準,則可以將它們放入這些內部查詢中。例如,您在評論中提到了基於子字符串搜索的選擇。你可以這樣做如下

   SELECT t.line_id,t.assembly_datetime datetime,... 
       FROM table_product_stereos AS t 
       JOIN table_manufacture_line AS m ON m.line_id = t.line_id 
                AND m.line_name LIKE '%test' 
       WHERE company_id=# 
       ORDER BY assembly_datetime DESC 
       LIMIT 100 

接下來,您使用UNION到這兩個查詢結果集合二爲一。 UNION具有消除重複的功能,這是耗時的。 (你知道你沒有重複的東西,但是MySQL沒有。)用UNION ALL代替。

把這一切放在一起,最內層的子查詢變成了這個。我們必須包裝子查詢,因爲SQL在相同的查詢級別被UNIONORDER BY子句混淆。

  SELECT * FROM (
       SELECT line_id,assembly_datetime datetime,... 
       FROM table_product_stereos 
       WHERE company_id=# 
       ORDER BY assembly_datetime DESC 
       LIMIT 100 
         ) AS st 
      UNION ALL 
      SELECT * FROM (
      SELECT line_id,assembly_datetime datetime,... 
       FROM table_product_televisions 
       WHERE company_id=# 
       ORDER BY assembly_datetime DESC 
       LIMIT 100 
         ) AS tv 

這會讓你200行。它應該很快得到這些行。

確保200行足以在您執行外部ORDER BY ... LIMIT操作後爲您提供100個最新項目。但是這個操作只需要緊縮200行,而不是100K +,所以它會更快。

最後在您的外部查詢材料中包含此查詢。加入table_manufacture_line信息,並修正時區。

如果您在早期執行索引和ORDER BY ... LIMIT操作,則此查詢應該變得非常快。

您問題中的評論對話框向我表明,您可能有多種產品類型,而不僅僅是兩種產品類型,並且您對分頁顯示器有複雜的選擇標準。在大量行上使用UNION ALL可以提高性能:它將多個索引錶轉換爲無法高效搜索的內部行列表。

您真的應該考慮將您的兩種產品數據放在一張表中,而不是必須使用UNION ALL多個產品表。您現在的設置不靈活,不會輕鬆擴展。如果您使用主產品表構建模式,並且可能使用某些特定於表的產品特定信息,則兩年後您會發現自己變得更加快樂。認真。請考慮進行更改。

+0

我還需要使用「WHERE line_name LIKE'%test'」來限制來自搜索字符串的line_name的查詢,並返回要顯示的line_name。在LIMIT之前可以在table_manufacture_line上添加一個INNER JOIN?此外,我必須返回基於分頁的結果,因此在UNION ALL之後,LIMIT不會完全適用於我,對嗎? – DemonGyro 2014-10-03 13:09:19

+0

Yipes! 'LIKE'%string''是你能做的最慢的查詢! – 2014-10-03 15:01:38

+0

不幸的是,它的一個要求。我正在處理分割查詢atm,我在單獨的查詢中獲取line_id和line_name字段(必須僅比較最大3-20行),然後將line_id值傳遞到UNION ALL查詢中,執行WHERE line_id IN()'。我將在PHP中對數據進行後處理,以便爲條目設置line_name,每個頁面最多有100個條目。 – DemonGyro 2014-10-03 15:13:57