2

4GB RAM、2CPU、60GB SSD の特性を持つ VPS で Prestashop 1.6 搭載の e-shop を実行しています。現在、私のショップには約 20000 個の製品があり、mysql クエリが長時間実行されるため、サイトの読み込みに問題があります。htopを実行してプロセスを分析すると、mysql が両方の CPU を 100% 消費していることがわかります。これはmysqltunerからの出力です:

-------- Performance Metrics -------------------------------------------------
[--] Up for: 1h 29m 9s (241K q [45.109 qps], 319 conn, TX: 318M, RX: 126M)
[--] Reads / Writes: 78% / 22%
[--] Total buffers: 192.0M global + 2.7M per thread (151 max threads)
[OK] Maximum possible memory usage: 597.8M (15% of installed RAM)
[OK] Slow queries: 0% (8/241K)
[OK] Highest usage of available connections: 2% (4/151)
[OK] Key buffer size / total MyISAM indexes: 16.0M/44.9M
[OK] Key buffer hit rate: 99.6% (36M cached / 133K reads)
[OK] Query cache efficiency: 49.6% (101K cached / 205K selects)
[!!] Query cache prunes per day: 1386761
[OK] Sorts requiring temporary tables: 0% (1 temp sorts / 7K sorts)
[!!] Joins performed without indexes: 78
[OK] Temporary tables created on disk: 8% (846 on disk / 9K total)
[OK] Thread cache hit rate: 98% (4 created / 319 connections)
[!!] Table cache hit rate: 10% (340 open / 3K opened)
[OK] Open file limit used: 62% (643/1K)
[OK] Table locks acquired immediately: 100% (239K immediate / 239K locks)

-------- Recommendations -----------------------------------------------------
General recommendations:
    Add skip-innodb to MySQL configuration to disable InnoDB
    MySQL started within last 24 hours - recommendations may be inaccurate
    Enable the slow query log to troubleshoot bad queries
    Adjust your join queries to always utilize indexes
    Increase table_cache gradually to avoid file descriptor limits
Variables to adjust:
    query_cache_size (> 16M)
    join_buffer_size (> 128.0K, or always use indexes with joins)
    table_cache (> 400)

最適化方法を提案してください。

編集:

スロークエリの出力ログはこちら.

4

2 に答える 2

0

使用時間が長くなった後は、mysqltuner を使用する必要があります。これにより、次のメッセージが削除されます: MySQL は過去 24 時間以内に開始されました - 推奨事項が不正確である可能性があります

于 2015-08-31T16:27:06.767 に答える
0

これはログからの 2 番目のクエリで、他のクエリを調べています。これを支援するために、サブクエリを取り除くために少し再構築し、INNER JOIN に変えました。また、他の人があなたのテーブルを知らないのを助けるために、フィールド参照を常に table.column または alias.column として修飾して、level_depth、nleft、nright がどこから来ているかを知るようにしてください。インデックスに役立つ場合があります。

インデックスの場合、テーブルごとに次のようになります。

ps_category, index ON( active, id_category, id_lang, nleft, nright, level_depth )
ps_category_lang, index ON( id_category, id_shop, id_lang )
ps_category_shop, index ON( id_category, id_shop )  
ps_category_group, index ON( id_category, id_group )
ps_lang, index ON( id_lang, active )

少し書き直したクエリ

SELECT 
      c.id_parent, 
      c.id_category, 
      cl.name, 
      cl.description, 
      cl.link_rewrite
   FROM 
      ps_category c
         INNER JOIN ps_category_lang cl 
            ON c.id_category = cl.id_category 
            AND cl.id_shop = 1
            AND cl.id_lang = 2 
         INNER JOIN ps_category_shop cs
            ON c.id_category = cs.id_category 
            AND cs.id_shop = 1
         INNER JOIN ps_category_group psg
            ON c.id_category = psg.id_category
            AND psg.id_group = 1
   WHERE 
      (  c.active = 1 
      OR c.id_category = 2)
      AND c.id_category != 1
      AND level_depth <= 7
      AND nleft >= 350 AND nright <= 351
   ORDER BY 
      level_depth ASC, 
      cs.position ASC;

3 番目のクエリ。この場合、特定の重要な要素の一部が一番上に移動されていることに注意してください。したがって、キー インデックスにもそれらがあり、残りは where 句にあります (主に読みやすさの調整ですが、「特定の」要素を確認するのに役立ちます)インデックスの利点 )

SELECT 
      c.id_category, 
      cl.name, 
      cl.link_rewrite
   FROM 
      ps_category c
         LEFT JOIN ps_category_lang cl 
            ON c.id_category = cl.id_category 
            AND cl.id_shop = 1
         INNER JOIN ps_category_shop category_shop
            ON c.id_category = category_shop.id_category 
            AND category_shop.id_shop = 1
   WHERE 
          c.active = 1
      AND cl.id_lang = 2
      AND c.nleft between 2 and 350
      AND c.nright between 351 and 625
      AND c.level_depth > 1
   ORDER BY 
      c.level_depth ASC;

あなたが本当に望んでいたものを確認する必要がある次のクエリ...言語テーブルへの左結合がありますが、実際にはそれをINNERに変えるwhere句に「AND l.active = 1」を追加します加入。本当にLEFT-JOINにしたい場合は、ここで調整したように l.active を結合部分に移動します

SELECT 
      l.id_lang, 
      c.link_rewrite
   FROM 
      ps_category_lang AS c
         LEFT JOIN ps_lang AS l 
            ON c.id_lang = l.id_lang
            AND l.active = 1
   WHERE 
      c.id_category = 324

これらのインデックスとサンプルの明確化/クエリの読みやすさが、ログの改善に役立つことを願っています。それでも問題が解決しない場合は、必要に応じて投稿してください。

スロー ログ レポートの最初のクエリの改訂

最初のクエリでは、特定の製品ショップから商品を探しているようです。ただし、製品カテゴリからクエリを開始し、製品に左結合し、次に製品ショップに結合しています。これらの製品は最終的に where を介して特定のカテゴリに関連付けられるため、いずれにしても INNER JOIN が作成されます。以下のように少し再構築します

ps_product_shop ON ( id_shop, active, visibility, id_product, id_category_default ) に単一のインデックスを作成します。

SELECT 
      p.*, 
      ps.*, 
      stock.out_of_stock, 
      IFNULL(stock.quantity, 0) as quantity, 
      MAX(pas.id_product_attribute) id_product_attribute, 
      pas.minimal_quantity AS product_attribute_minimal_quantity, 
      pl.description, 
      pl.description_short, 
      pl.available_now,
      pl.available_later, 
      pl.link_rewrite, 
      pl.meta_description, 
      pl.meta_keywords, 
      pl.meta_title, 
      pl.name, 
      MAX(image_shop.id_image) id_image,
      il.legend, 
      m.name AS manufacturer_name, 
      cl.name AS category_default,
      DATEDIFF(ps.`date_add`, DATE_SUB(NOW(),INTERVAL 20 DAY)) > 0 AS new, 
      ps.price AS orderprice
   FROM 
      ( select @thisLanguage := 1 ) sqlvars,
      ps_product_shop ps
         INNER JOIN ps_product p
            ON ps.id_product = p.id_product

            INNER JOIN ps_category_product cp
               ON id_product = cp.id_product
               AND cp.id_category = 2 

            LEFT JOIN ps_product_attribute pa
               ON p.id_product = pa.id_product
               LEFT JOIN ps_product_attribute_shop pas
                  ON pa.id_product_attribute = pas.id_product_attribute
                  AND ps.id_shop = pas.id_shop
                  AND pas.default_on = 1

            LEFT JOIN ps_stock_available stock
               ON p.id_product = stock.id_product 
               AND ps.id_shop = stock.id_shop
               AND stock.id_shop_group = 0
               AND stock.id_product_attribute = IFNULL(pas.id_product_attribute, 0)

            LEFT JOIN ps_product_lang pl
               ON p.id_product = pl.id_product
               AND ps.id_shop = pl.id_shop 
               AND pl.id_lang = @thisLanguage

            LEFT JOIN ps_image i
               ON p.id_product = i.id_product
               LEFT JOIN ps_image_shop image_shop
                  ON i.id_image = image_shop.id_image 
                  AND ps.id_shop = image_shop.id_shop
                  AND image_shop.cover = 1
                  LEFT JOIN ps_image_lang il
                     ON image_shop.id_image = il.id_image
                     AND il.id_lang = @thisLanguage

            LEFT JOIN ps_manufacturer m
               ON p.id_manufacturer = m.id_manufacturer

         LEFT JOIN ps_category_lang cl
            ON ps.id_category_default = cl.id_category
            AND cl.id_shop = ps.id_shop
            AND cl.id_lang = @thisLanguage
   WHERE 
          ps.id_shop = 1
      AND ps.active = 1 
      AND ps.visibility IN ("both", "catalog") 
   GROUP BY 
      ps.id_product 
   ORDER BY 
      cp.position ASC
   LIMIT 
      0,8;

複数フィールドのインデックスを単一のインデックスとして作成する場合は、次のようになります。

CREATE INDEX act_id_lang ON ps_category( active, id_category, id_lang, nleft, nright, level_depth );

簡単なインデックス名を付けます...どのテーブルでも...クエリで有効になるため、すべてのキーを使用します。

于 2014-10-21T13:29:36.397 に答える