【问题标题】:Optimize MySQL for Prestashop 1.6为 Prestashop 1.6 优化 MySQL
【发布时间】:2014-10-21 09:22:49
【问题描述】:

我在具有 4GB RAM、2CPU、60GB SSD 特性的 VPS 上运行基于 Prestashop 1.6 的电子商店。目前,我的商店中有大约 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)

请提出任何优化方法。

编辑:

慢查询输出日志为here

【问题讨论】:

  • 所以基本上你是在免费寻求专业的调优帮助,而且你不知道从哪里开始优化?
  • 如何获取慢查询日志并发布导致性能下降的实际罪魁祸首 SQL 查询。一堆统计数据与没有看到编写不佳的查询和已知索引可能真的是事情令人窒息的地方。我不知道我是否会同意所示的 0% 的慢速查询。注意你的 JOINS WITHOUT INDEXES = 78
  • @DRapp 这里是慢查询日志的输出pastie.org/9664946
  • @N.B.如果我知道我就不会问了。不管是不是专业调音,我都有学习的权利。
  • 当然你有学习的权利,我永远不会质疑这一点。但是,您发布了一个程序的输出,然后您期望用勺子喂食?例如,程序会告诉您:Enable the slow query log to troubleshoot bad queries。谷歌 > “启用慢查询日志以解决错误查询” > 如果遇到问题 - 在此处询问。这样,您似乎希望一切都为您完成,因为您提出的问题没有足够的信息来帮助您。看起来您想要一个完整的解决方案,而不是解决问题的线索。

标签: mysql query-optimization prestashop database-administration mysqltuner


【解决方案1】:

这是您日志中的第二个查询,正在查看其他查询。为了帮助解决这个问题,我稍微重组以摆脱子查询并变成 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;

第三次查询。请注意,在这种情况下,某些特定的关键元素被移到顶部,因此我也将它们放在键索引中,然后将其余部分放在 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;

您需要确认您真正想要的下一个查询...您对语言表有一个左连接,但随后在 where 子句中添加“AND l.active = 1”,这实际上会变成它进入 INNER JOIN。如果你真的想要一个 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);

给出一个简单的索引名称...在任何表上...使用所有键,因为它们将在查询中有效。

【讨论】:

  • 对于第二个查询,我有 Query_time: 0.001116。为什么我需要优化它?第一个查询很长Query_time: 11.635315
  • @torayeff,我将回顾第一个查询,但索引本身应该会有所帮助,并且所写的查询向我展示/帮助我确定哪些索引可能最能全面帮助.我很快就会回到第一个查询。
  • @torayeff,这可能是......但它们是单独的索引,还是我列为单一索引的所有字段的复合索引......查询时完全不同的结果。跨度>
  • 我可以让你访问我的 phpmyadmin 吗?
  • 这不是索引问题。使用任何索引都不存在令人满意的标准,而且基数明显较低。这是一个简单的配置问题,他使用的是 MyISAM 引擎,数据库必须经过 20k 条记录。在磁盘上排序,即使是 SSD,也比使用 RAM 慢。它也是一个网上商店,在没有事务支持的情况下运行。这里涉及很多错误,这是一个烫手山芋。我建议聘请专家,从长远来看会便宜得多,而且你显然不是来这里学习的。
【解决方案2】:

您应该在较长的使用时间后使用 mysqltuner。 这应该删除以下消息: MySQL 在过去 24 小时内启动 - 建议可能不准确

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-10-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-17
    • 1970-01-01
    相关资源
    最近更新 更多