2

私はMySQLの専門家ではありませんが、かなり大きなテーブル(600,000行と約90列(私を殺してください...))を継承し、それをリンクするために作成した小さなテーブルがあります。カテゴリテーブル。

左結合を使用して上記のテーブルをクエリしようとしているので、1つのオブジェクトに両方のデータセットがありますが、実行速度が非常に遅く、整理するのに十分な熱さではありません。なぜこんなに遅いのか、ちょっとしたガイダンスと説明をいただければ幸いです。

SELECT 
    `products`.`Product_number`,
    `products`.`Price`,
    `products`.`Previous_Price_1`,
    `products`.`Previous_Price_2`,
    `products`.`Product_number`,
    `products`.`AverageOverallRating`,
    `products`.`Name`,
    `products`.`Brand_description`
FROM `product_categories`
LEFT OUTER JOIN `products`
ON `products`.`product_id`= `product_categories`.`product_id`
WHERE COALESCE(product_categories.cat4, product_categories.cat3,
product_categories.cat2, product_categories.cat1) = '123456'
AND `product_categories`.`product_id` != 0

2つのテーブルはMyISAMであり、productsテーブルにはProduct_numberとBrand_Descriptionにインデックスがあり、product_categoriesテーブルにはすべての列を組み合わせた一意のインデックスがあります。この情報がまったく役に立たない場合。

このシステムを継承したので、私はそれを核兵器にし、それを適切に行う前に、これをできるだけ早く機能させる必要があります。

[編集]explain拡張の出力は次のとおりです。

+----+-------------+--------------------+-------+---------------+------+---------+------+---------+----------+--------------------------+
| id | select_type | table              | type  | possible_keys | key  | key_len | ref  | rows    | filtered | Extra                    |
+----+-------------+--------------------+-------+---------------+------+---------+------+---------+----------+--------------------------+
|  1 | SIMPLE      | product_categories | index | NULL          | cat1 | 23      | NULL | 1224419 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | products           | ALL   | Product_id    | NULL | NULL    | NULL |  512376 |   100.00 |                          |
+----+-------------+--------------------+-------+---------------+------+---------+------+---------+----------+--------------------------+
4

3 に答える 3

3

テーブルの最適化

ベースラインを確立するには、最初OPTIMIZE TABLEに両方のテーブルでコマンドを実行することをお勧めします。これには時間がかかる場合がありますのでご注意ください。ドキュメントから:

OPTIMIZE TABLEテーブルの大部分を削除した場合、または可変長の行を持つテーブル(VARCHAR, VARBINARY, BLOB、またはTEXT列を持つテーブル)に多くの変更を加えた場合に使用する必要があります。削除された行はリンクリストに保持され、後続のINSERT 操作は古い行の位置を再利用します。を使用OPTIMIZE TABLEして、未使用のスペースを再利用し、データファイルを最適化できます。テーブルに大幅な変更を加えた後、このステートメントは、テーブルを使用するステートメントのパフォーマンスを大幅に向上させる場合もあります。

[...]

MyISAMテーブルの場合、次のようにOPTIMIZE TABLE機能します。

  1. テーブルで行が削除または分割されている場合は、テーブルを修復します。

  2. インデックスページが並べ替えられていない場合は、並べ替えます。

  3. テーブルの統計が最新でない場合(およびインデックスをソートしても修復を実行できなかった場合)、それらを更新します。

インデックス作成

スペースとインデックスの管理が問題にならない場合は、複合インデックスを追加してみてください。

product_categories.cat4, product_categories.cat3, product_categories.cat2, product_categories.cat1

これらの列の左端のサブセットをクエリで頻繁に使用する場合は、これをお勧めします。クエリプランは、のcat1インデックスを使用できることを示していますproduct_categories。ほとんどの場合、これにはcat1列のみが含まれます。4つのカテゴリ列すべてをインデックスに追加することで、目的の行をより効率的に検索できます。ドキュメントから:

MySQLは、インデックス内のすべての列をテストするクエリ、または最初の列、最初の2列、最初の3列などだけをテストするクエリに複数列のインデックスを使用できます。インデックス定義で正しい順序で列を指定すると、単一の複合インデックスで同じ テーブルに対する複数の種類のクエリを高速化できます。

構造

さらに、テーブルに90列がある場合、テーブルの幅が広いとクエリのパフォーマンスが低下する可能性があることにも注意する必要があります。テーブルを複数のテーブルに垂直に分割することを検討することをお勧めします。

列が多すぎると、レコードサイズが肥大化する可能性があり、その結果、より多くのメモリブロックがメモリに読み込まれ、メモリから読み取られて、I/Oが高くなります。これにより、パフォーマンスが低下する可能性があります。これに対抗する1つの方法は、テーブルを元のテーブルよりもカーディナリティが小さい、より小さな独立したテーブルに分割することです。これにより、(上記で定義した)より優れたブロッキング係数が可能になり、I/Oが少なくなりパフォーマンスが向上します。このようにテーブルを分割するこのプロセスは、垂直パーティションと呼ばれます。

于 2012-10-14T17:04:10.550 に答える
1

クエリの意味は、「カテゴリ「123456」のすべての製品を検索する」のようです。あれは正しいですか?

COALESCEWHEREは、インデックスに敵対するNULL値で動作するため、ステートメントで使用するのに非常にコストのかかる関数です。Explainの結果は、クエリがproduct_categoriesテーブルであまり選択的ではないことを示しています。MySQLでは、インデックスを利用してクエリを高速化する場合は、WHEREステートメントの関数を完全に回避する必要があります。

他の誰かが90列のテーブルが有害であると言ったことも真実です。しかし、あなたはそれに固執しているので、それを処理しましょう。

関数ベースを取り除くためにクエリを作り直すことはできますWHEREか?これを試してみましょう。

SELECT  /* some columns from the products table */
  FROM products
 WHERE product_id IN 
 (
     SELECT DISTINCT product_id 
                FROM product_categories
               WHERE product_id <> 0
                 AND (   cat1='123456'
                      OR cat2='123456'
                      OR cat3='123456'
                      OR cat4='123456')
 )

これを高速に機能させるには、4つの猫の列に個別のインデックスを作成する必要があります。複合一意インデックス(「すべての列を組み合わせたもの」)は役に立ちません。それでもあまり良くないかもしれません。

より良い解決策は、ブールモードでの全文検索です。MyISAMアクセス方式を使用しているため、これが可能です。それは間違いなく試してみる価値があります。それは確かに非常に速いかもしれません。

SELECT  /* some columns from the products table */
  FROM products
 WHERE product_id IN 
 (
     SELECT product_id 
       FROM product_categories
      WHERE MATCH(cat1,cat2,cat3,cat4) 
            AGAINST('123456' IN BOOLEAN MODE)
        AND product_id <> 0

 )

これを高速に機能させるには、そのようなFULLTEXTインデックスを作成する必要があります。

 CREATE FULLTEXT INDEX cat_lookup 
                    ON product_categories (cat1, cat2, cat3, cat4)

これらの提案されたクエリはどちらも、クエリとまったく同じ結果を生成しないことに注意してくださいCOALESCE。クエリの設定方法COALESCEによっては、これらのクエリに一致する組み合わせが一致しない場合があります。例えば。

    cat1     cat2     cat3     cat4   
  123451   123453   123455   123456      matches your and my queries
  123456   123455   123454   123452      matches my queries but not yours

しかし、私のクエリでは、アイテムがさらにいくつかある場合でも、有用な製品のリストが生成される可能性があります。

product_categoriesの内部クエリを操作するだけで、このようなものをデバッグできます。

于 2012-10-14T19:39:22.677 に答える
0

奇妙なことがあります。テーブルproduct_categoriesには実際にproduct_id列がありますか?fromandwhere句は次のようにすべきではありません:

FROM `product_categories` pc 
LEFT OUTER JOIN `products` p ON p.category_id = pc.id
WHERE 
    COALESCE(product_categories.cat4, product_categories.cat3,product_categories.cat2, product_categories.cat1) = '123456'
    AND pc.id != 0
于 2012-10-14T17:08:17.797 に答える