4

現在の実装は、複数の結合と一時テーブルを持つ単一の複雑なクエリですが、MySQL に過度の負荷をかけ、テーブルのロードに 30 秒以上かかっています。データは、JavaScript Ajax 呼び出しを介して PHP によって取得され、Web ページに表示されます。関連するテーブルは次のとおりです。

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, ...

1 つの会社で、2 つの製品テーブルに分割された 10 万以上のアイテムを持つことができます。製品テーブルは line_name で結合およびフィルター処理され、次に assembly_datetime で並べ替えられ、ページングに応じて制限されます。日時の値もタイムゾーンに依存しており、これはクエリの一部として適用されます (別の JOIN + 一時テーブル)。line_name も返される列の 1 つです。

製品ユニオン クエリから line_name フィルターを分割することを考えていました。基本的に、フィルターに対応する行の ID を特定してから、WHERE 条件を使用して UNION クエリを実行しますWHERE line_id IN (<results from previous query>)。これにより、結合と一時テーブルの必要性がなくなり、line_name を line_id に適用し、PHP でタイムゾーンを変更できますが、これが最善の方法であるかどうかはわかりません。

Redis を使用する可能性についても調べましたが、多数の個々の製品により、PHP を介してすべてのデータを 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 は索引付けされます。主キーは、各列の最初のキーです。

4

3 に答える 3

2

このクエリを構成要素から構築して、最適化できるものを見てみましょう。

観察: 2 つの大きな製品テーブルの和集合から最新の 100 行を取得しています。

それでは、製品テーブルから何かを取得するサブクエリを最適化することから始めましょう。ここにそれらの1つがあります。

              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これら 2 つのクエリ結果セットを 1 つに結合するために使用しています。 UNION時間のかかる重複を排除する機能があります。(重複がないことはわかっていますが、MySQL にはありません。)UNION ALL代わりに使用してください。

これをまとめると、一番内側のサブクエリはこうなります。SQL は同じクエリ レベルのUNIONand句によって混同されるため、サブクエリをラップする必要があります。ORDER 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 行になります。これらの行をかなり迅速に取得する必要があります。

外側の操作を行った後、最新の 100 個のアイテムを取得するには、200 行で十分であることが保証されていますORDER BY ... LIMIT。しかし、その操作は 100K+ ではなく 200 行を処理するだけでよいため、はるかに高速になります。

最後に、このクエリを外側のクエリ マテリアルにまとめます。情報を結合table_manufacture_lineし、タイムゾーンを修正します。

インデックス作成とORDER BY ... LIMIT操作を先に行うと、このクエリは非常に高速になるはずです。

あなたの質問のコメント ダイアログは、2 つだけでなく複数の製品タイプを持っている可能性があり、ページ表示の選択基準が複雑であることを示しています。UNION ALL多数の行で使用すると、パフォーマンスが大幅に低下します。複数のインデックス付きテーブルを、効率的に検索できない行の内部リストに変換します。

UNION ALL2 種類の製品データを複数の製品テーブルではなく、1 つのテーブルに配置することを検討する必要があります。現在のセットアップは柔軟性がなく、簡単にスケールアップすることはできません。マスター製品テーブルと、おそらく製品固有の情報用のいくつかの属性テーブルを使用してスキーマを構築すると、今から 2 年後にはもっと幸せになれるでしょう。真剣に。変更をご検討ください。

于 2014-10-03T01:45:38.157 に答える
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=#)

そこから製品を選択できます。またはミリリットル。必要に応じてフィールド。

于 2014-10-02T22:41:47.383 に答える