Оптимизация MySQL для Prestashop 1.6

Я запускаю интернет-магазин на базе Prestashop 1.6 на VPS с 4 ГБ ОЗУ, 2 процессорами и характеристиками SSD на 60 ГБ. В настоящее время в моем магазине около 20000 товаров, и у меня проблемы с загрузкой сайта, потому что запросы mysql выполняются долго. И когда я запускаю htop для анализа процессов, я вижу, что mysql потребляет 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)

Пожалуйста, предложите любые методы оптимизации.

ИЗМЕНИТЬ:

Журнал медленных запросов находится здесь.


person torayeff    schedule 21.10.2014    source источник
comment
Итак, вы ищете бесплатную профессиональную помощь в настройке, плюс вы не знаете, с чего начать оптимизацию?   -  person N.B.    schedule 21.10.2014
comment
Как насчет того, чтобы получить журнал медленных запросов и опубликовать актуальные SQL-запросы-виновники, которые убивают производительность. Куча статистики по сравнению с отсутствием плохо написанных запросов и известных индексов может действительно быть тем, где все задыхается. Я не знаю, соглашусь ли я с медленными запросами 0% процентов, как указано. Обратите внимание, что ваши JOINS WITHOUT INDEXES = 78   -  person DRapp    schedule 21.10.2014
comment
@DRapp вот вывод журнала медленных запросов pastie.org/9664946   -  person torayeff    schedule 21.10.2014
comment
@Н.Б. Если бы знал, то не спрашивал бы. Будь то профессиональный тюнинг или нет, я имею право учиться.   -  person torayeff    schedule 21.10.2014
comment
Конечно, ты имеешь право учиться, я бы никогда в этом не сомневался. Тем не менее, вы опубликовали вывод программы, и что, вы ожидаете кормления с ложки? Например, программа сообщает вам следующее: Enable the slow query log to troubleshoot bad queries. Google › Включите журнал медленных запросов, чтобы устранять неполадки с неверными запросами › если возникнут проблемы, спросите здесь. Таким образом, создается впечатление, что вы хотите, чтобы все было сделано за вас, потому что вопрос, который вы задали, не содержит достаточно информации, чтобы помочь вам. Похоже, вам нужно полное решение, а не подсказки о том, как подойти к проблеме.   -  person N.B.    schedule 21.10.2014
comment
@Н.Б. Хорошо, я не хочу спорить. Если вы можете помочь.   -  person torayeff    schedule 21.10.2014
comment
Начните с включения журнала медленных запросов. Он будет регистрировать запросы, выполнение которых занимает слишком много времени. Если у вас их несколько, и если они типа SELECT, добавьте EXPLAIN перед SELECT, чтобы увидеть план выполнения. Вы делаете это из терминала MySQL или PHPMyAdmin (или любого другого графического интерфейса MySQL). Он расскажет вам, что MySQL пытается сделать внутри для выполнения запроса. После этого разместите здесь запросы вместе со структурой таблицы для дальнейшего анализа.   -  person N.B.    schedule 21.10.2014
comment
@N.B Этот запрос, который я вставил сюда pastie.org/9665033, выполняется около 12 секунд только для отображения 8 записей. .   -  person torayeff    schedule 21.10.2014
comment
Можно ли как-то кешировать запросы?   -  person torayeff    schedule 21.10.2014
comment
Сам по себе запрос бесполезен сам по себе. Нам нужно увидеть план выполнения. Добавьте EXPLAIN перед выбором вот так: EXPLAIN SELECT и опубликуйте это обратно.   -  person N.B.    schedule 21.10.2014
comment
Давайте продолжим обсуждение в чате.   -  person torayeff    schedule 21.10.2014
comment
объяснение выбора выходных данных находится здесь pastie.org/9665152   -  person torayeff    schedule 21.10.2014
comment
Я думаю, что этот вопрос поможет вам stackoverflow.com /вопросы/26483174/   -  person HMagdy    schedule 19.02.2015


Ответы (2)


Вот второй запрос из вашего журнала, просмотр других. Чтобы помочь в этом, я немного реструктурировал, чтобы избавиться от подзапроса и превратился во ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Кроме того, чтобы помочь другим, не знающим ваши таблицы, всегда определяйте ссылки на поля как 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;

В следующем запросе вам нужно подтвердить, что вы действительно хотели... У вас есть левое соединение с языковой таблицей, но затем добавьте «AND l.active = 1» в предложении where, что на самом деле превратит его в INNER ПРИСОЕДИНИТЬСЯ. Если вы действительно хотите, чтобы это было 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, в любом случае создается ВНУТРЕННЕЕ СОЕДИНЕНИЕ. Я бы немного реструктурировал, как показано ниже

Я бы включил один индекс для ps_product_shop ( 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;

Что касается создания индекса нескольких полей как единого индекса, например:

СОЗДАТЬ ИНДЕКС act_id_lang ON ps_category(active, id_category, id_lang, nleft, nright, level_depth);

дайте простое имя индекса... для любой таблицы... со ВСЕМИ ключами, поскольку они будут эффективны в запросе.

person DRapp    schedule 21.10.2014
comment
Для второго запроса у меня есть Query_time: 0,001116. Зачем мне его оптимизировать? Первый запрос длинный Query_time: 11.635315 - person torayeff; 21.10.2014
comment
@torayeff, я вернусь к первому запросу, но сами индексы должны помочь, а запросы в том виде, в котором они написаны, показывают мне / помогают мне определить, какие индексы могут лучше всего помочь по всем направлениям. Я скоро вернусь к первому запросу. - person DRapp; 21.10.2014
comment
@torayeff, это может быть ... но являются ли они отдельными индексами или СОСТАВНЫМ индексом всех полей, которые я указал как ЕДИНСТВЕННЫЙ индекс ... Совершенно разные результаты при запросе. - person DRapp; 21.10.2014
comment
могу ли я дать вам доступ к моему phpmyadmin? - person torayeff; 21.10.2014
comment
Я имел в виду, как я могу дать вам доступ к моему phpmyadmin? Куда я могу отправить вас? - person torayeff; 21.10.2014
comment
@torayeff, сейчас не могу помочь удаленно, но добавил в ответ на первый реструктурированный запрос и образец синтаксиса для создания индекса с несколькими столбцами, чтобы помочь. - person DRapp; 21.10.2014
comment
Это не проблема индекса. Не существует удовлетворяющих критериев для использования какого-либо индекса, к тому же кардинальность явно мала. Это простая проблема с конфигурацией, он использует движок MyISAM, и база данных должна пройти через 20 тысяч записей. Сортировка на диске, даже если SSD, медленнее, чем использование оперативной памяти. Это также интернет-магазин, работающий без поддержки транзакций. Здесь много ошибок, это горячая картошка. Я предлагаю нанять эксперта, в долгосрочной перспективе это намного дешевле, и вы, очевидно, здесь не для того, чтобы учиться. - person N.B.; 21.10.2014

Вы должны использовать mysqltuner после более длительного времени использования. Это должно удалить следующее сообщение: MySQL запущен в течение последних 24 часов — рекомендации могут быть неточными.

person jmrenouard    schedule 31.08.2015