[Помогите] Как понять что это за запрос MySql

Тема в разделе "OpenCart", создана пользователем cereberlum, 10 сен 2014.

  1. cereberlum

    cereberlum

    Регистрация:
    26 май 2013
    Сообщения:
    756
    Симпатии:
    269
    Этот запрос сильнее всего нагружает сайт, по сему периодически слетает соединение с базой (Джино) скажите что это за запрос и как бы его ускорить? Спасибо
    Код:
    SELECT COUNT(DISTINCT p.product_id) AS total FROM oc_category_path cp LEFT JOIN oc_product_to_category p2c ON (cp.category_id = p2c.category_id) LEFT JOIN oc_product p ON (p2c.product_id = p.product_id) LEFT JOIN oc_product_description pd ON (p.product_id = pd.product_id) LEFT JOIN oc_product_to_store p2s ON (p.product_id = p2s.product_id) WHERE pd.language_id = '2' AND p.status = '1' AND p.date_available <= '2014-09-07 23:16:00' AND p2s.store_id = '0' AND cp.path_id = '104';
     
  2. nix

    nix php, MySQL, UNIX, MikroTik ROSAPI

    Регистрация:
    16 янв 2013
    Сообщения:
    1.000
    Симпатии:
    890
    Етот запрос подсчитывает к-во товаров в категории
    и сократить его не получится, ну как нет, получится если переписать сам опенкарт и его структуру БД )
    Вот посмотрите на условия в запросе
    WHERE pd.language_id = '2' делает выборку из oc_product_description для определения языка продукта(например переключил человек на русский язык а там нет товара который есть на укр, вот для правильного подсчета нужно ето условия)
    AND p.status = '1' выборка из oc_product - проверяет статус твоара, включен ли он
    AND p.date_available <= '2014-09-07 23:16:00' выборка из oc_product, проверяет доступен ли товар уже, может быть такое что товар будет например доступен 2015 года а добавили еще в 2014 вот для етого и нужно ето условия
    AND p2s.store_id = '0' из oc_product_to_store, проверяет принадлежит ли товар магазину з определеным ИД, глупо так как ниразу не видел мультимагазина на опенкарт но все же правильно
    AND cp.path_id = '104'" ет из c_category_path, вопше главное условия,оно как раз и считает к-во товаров в категории проходящей ето условия(с таким ИД)
    минус етого запроса не только в том что он тежелый, но то что он выполняется в цикле
     
    Последнее редактирование: 10 сен 2014
    cereberlum нравится это.
  3. cereberlum

    cereberlum

    Регистрация:
    26 май 2013
    Сообщения:
    756
    Симпатии:
    269
    Было такое подозрение, это нормально что при 1700 товаров слетает соединение с БД? Как бы у меня включен подсчет товаров в модуле категорий и в верхнем меню, но отключать не хочу, какие варианты спасения ситуации?
     
  4. nix

    nix php, MySQL, UNIX, MikroTik ROSAPI

    Регистрация:
    16 янв 2013
    Сообщения:
    1.000
    Симпатии:
    890
    давно не делал чего то на опенкарте, но помню что были времена когда 700 к товаров тянул на опенкарте без проблем(конечно после оптимизации всего копипаста цыклов опенкарта в коде и т.д...)
    И момент такой, здесь какбы и не к-во товаров важную роль играет а наоборот - к-во категорий, чем больше категорий и чем больше родителей у них тем тяжелей себя чуствует сервер БД, в ето время он и не дает хорошо жить самому интерпретатору, так как мускуль сделан по принцыпу такому, что часть (почти вся) которую выбрал, нужно еще обработать самим пхп и вот например для человеского ума: если запрос содержит выбраных даных на 20 Мб то они не только хранятся в памяти "мускульного процеса" но и + 20 Мб в память самому интерпретатору, а ето уже 40... и т.д...
    короче тут проще выключить подсчет или придумать свой алгоритм...
     
    cereberlum нравится это.
  5. cereberlum

    cereberlum

    Регистрация:
    26 май 2013
    Сообщения:
    756
    Симпатии:
    269
    Клиент ни в какую не хочет отключать злополучный подсчет.... а категорий действительно достаточно много. А нет каких-то вариантов кешировать этот запрос, ну скажем так чтобы он выполнялся не при каждом обновлении страницы, а скажем раз в час?
     
  6. nix

    nix php, MySQL, UNIX, MikroTik ROSAPI

    Регистрация:
    16 янв 2013
    Сообщения:
    1.000
    Симпатии:
    890
    можно например стандартным кешом опена воспользоватся
    вот наброс
    PHP:
    $total_p_cached $this->cache->get('total.p.' $lid '.' $sid);

           if (!
    $total_p_cached) {
             
    $total_p_cached = array();

             
    $query $this->db->query("

               здесь пишем запрос свой

             "
    );

             
    $total_p_cached $query->rows;

             
    $this->cache->set('total.p.' $lid '.' $sid$total_p_cached);
           }
    здесь 2 переменные
    $lid - ет так назвал ИД языка
    $sid - ИД магазина
     
  7. Baco

    Baco Антихронофаг Команда форума

    Регистрация:
    9 окт 2012
    Сообщения:
    803
    Симпатии:
    399
    я бы выбросил (имхо) 2-ва подзапроса:
    Код:
    LEFT JOIN oc_product_description pd ON (p.product_id = pd.product_id) 
    и
    Код:
    pd.language_id = '2'
            AND 
    ведь если по логике, то описание товара не влияет на выборку от языка товара, он по любому будет, вот только в подзапросе он путается, но зачем - не пойму.
    как по мне, такой вот запрос будет более оптимизированным:
    PHP:
    SELECT COUNT(DISTINCT p.product_id) AS total FROM oc_category_path cp
        LEFT JOIN oc_product_to_category p2c ON 
    (cp.category_id p2c.category_id)
        
    LEFT JOIN oc_product p ON (p2c.product_id p.product_id)
        
    LEFT JOIN oc_product_to_store p2s ON (p.product_id p2s.product_id)
            
    WHERE p.status '1'
            
    AND p.date_available <= '2014-09-07 23:16:00'
            
    AND p2s.store_id '0' AND cp.path_id '104'
    ;
     
    cereberlum нравится это.
  8. cereberlum

    cereberlum

    Регистрация:
    26 май 2013
    Сообщения:
    756
    Симпатии:
    269
    Насколько понимаю Это изменение касается модели продукта. + переменные языка и магазина подставил (как получилось) ....Теперь о том к какому именно запросу это относится, интуитивно полагаю что запрос должен быть где-то в методе ГетТоталПродуктс и если решение работает то в систем/кеш должен появляться файлик название которого содержит "total.p."
    PHP:
    $total_p_cached $this->cache->get('total.p.' .(int)$this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id'));

           if (!
    $total_p_cached) {
             
    $total_p_cached = array();

             
    $query $this->db->query("

               здесь пишем запрос свой

             "
    );

             
    $total_p_cached $query->rows;

             
    $this->cache->set('total.p.' . (int)$this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id'), $total_p_cached);
           }
    - Это правильно? теперь основной вопрос, о каком именно запросе речь?
    PHP:
    $sql .= " LEFT JOIN " DB_PREFIX "product_description pd ON (p.product_id = pd.product_id) LEFT JOIN " DB_PREFIX "product_to_store p2s ON (p.product_id = p2s.product_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p.status = '1' AND p.date_available <= '" $this->NOW "' AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "'";
    - этот?
     
  9. Baco

    Baco Антихронофаг Команда форума

    Регистрация:
    9 окт 2012
    Сообщения:
    803
    Симпатии:
    399
    Должен появится файл кеша по типу:
    "total.p.0.0"
    Речь идёт о запросе, приведённом в стартовом сообщении топика, напишите под спойлером метод, который выполняет этот запрос и я отпишу правильную оптимизацию по своему усмотрению...
     
    cereberlum нравится это.
  10. cereberlum

    cereberlum

    Регистрация:
    26 май 2013
    Сообщения:
    756
    Симпатии:
    269
    Вот фрагментс запросом, спасибо
     
  11. Baco

    Baco Антихронофаг Команда форума

    Регистрация:
    9 окт 2012
    Сообщения:
    803
    Симпатии:
    399
    PHP:
    $sql "SELECT COUNT(DISTINCT p.product_id) AS total";

    if (!empty(
    $data['filter_category_id'])) {
    if (!empty(
    $data['filter_sub_category'])) {
    $sql .= " FROM " DB_PREFIX "category_path cp LEFT JOIN " DB_PREFIX "product_to_category p2c ON (cp.category_id = p2c.category_id)";
    } else {
    $sql .= " FROM " DB_PREFIX "product_to_category p2c";
    }

    if (!empty(
    $data['filter_filter'])) {
    $sql .= " LEFT JOIN " DB_PREFIX "product_filter pf ON (p2c.product_id = pf.product_id) LEFT JOIN " DB_PREFIX "product p ON (pf.product_id = p.product_id)";
    } else {
    $sql .= " LEFT JOIN " DB_PREFIX "product p ON (p2c.product_id = p.product_id)";
    }
    } else {
    $sql .= " FROM " DB_PREFIX "product p";
    }

    $sql .= " LEFT JOIN " DB_PREFIX "product_to_store p2s ON (p.product_id = p2s.product_id) WHERE p.status = '1' AND p.date_available <= '" $this->NOW "' AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "'";
     
    cereberlum нравится это.
  12. nix

    nix php, MySQL, UNIX, MikroTik ROSAPI

    Регистрация:
    16 янв 2013
    Сообщения:
    1.000
    Симпатии:
    890
    оригинал
    PHP:
    $sql .= " LEFT JOIN " DB_PREFIX "product_description pd ON (p.product_id = pd.product_id) LEFT JOIN " DB_PREFIX "product_to_store p2s ON (p.product_id = p2s.product_id) WHERE pd.language_id = '" . (int)$this->config->get('config_language_id') . "' AND p.status = '1' AND p.date_available <= '" $this->NOW "' AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "'";
    "Оптимизированый"
    PHP:
    $sql .= " LEFT JOIN " DB_PREFIX "product_to_store p2s ON (p.product_id = p2s.product_id) WHERE p.status = '1' AND p.date_available <= '" $this->NOW "' AND p2s.store_id = '" . (int)$this->config->get('config_store_id') . "'";
    убрать один джоин из product_description ето не самое мудрое решения, смотрим выше написал за что оно отвечает.
    Во первых етот джоин ето как економия на спичках.
    А теперь детально что в последствии может быть:
    если в начале у Вас магазин на одном языке, естественно товары только для одного языка, например есть русский язык в начале и Вы добавлили товар "Утюг" потом добавили укр язык и добавили товар "Нож" при етом уже заполнили его на 2 языках!
    Теперь что происходит на сайте от етой псевдо оптимизации: если по дефолту есть язык ру, то клиент заходит в категорию и видит что пишет около нее к-во товаров равно 2, верно, так как вначале вы добавили утюг с русским языком но без укр и нож на двух языках. При етом еще и паджинация пишет что здесь мол есть 2 товара на странице
    Теперь человек переключает язык на укр и видет опять же к-во 2 и паджинация 2, но при етом на витрине показывается уже только один товар а именно НОЖ!
    вот почему ето нельзя делать!
    тут лучше убрать джоин из таблицы product_to_store так как витрина явно у большенства 1

    и кешировать можно загнав код под условия выше
    вот например так
    ищим вконце функции getTotalProducts
    строки
    PHP:
    $query $this->db->query($sql);
       
    return 
    $query->row['total'];
    и меняем на

    PHP:
         if (!empty($data['filter_category_id'])) {
           
    $cid = (int)$data['filter_category_id'];
         } else {
           
    $cid 0;
         }

         
    $total_p_cached $this->cache->get('total.p.' .(int)$this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id') . '.' $cid);

         if (!
    $total_p_cached) {

           
    $total_p_cached = array();
           
    $query $this->db->query($sql);
           
    $total_p_cached $query->row['total'];
           
    $this->cache->set('total.p.' . (int)$this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id') . '.' $cid$total_p_cached);
         }

         return 
    $total_p_cached;

    не проверял, набросал на глаз
     
    cereberlum нравится это.
  13. Baco

    Baco Антихронофаг Команда форума

    Регистрация:
    9 окт 2012
    Сообщения:
    803
    Симпатии:
    399
    Оптимизацию делал под условие, что на сайте 1 язык.
     
    cereberlum нравится это.
  14. nix

    nix php, MySQL, UNIX, MikroTik ROSAPI

    Регистрация:
    16 янв 2013
    Сообщения:
    1.000
    Симпатии:
    890
    ну да понятно как бы из запроса, да вот только 1 джоин меньше "ничего" особого не даст
    но условия такого небыло
    лучше делать как выше написал под условия что один магазин на сайте(ето боле вероятно чем 1 язык)
     
    cereberlum нравится это.
  15. Baco

    Baco Антихронофаг Команда форума

    Регистрация:
    9 окт 2012
    Сообщения:
    803
    Симпатии:
    399
    я бы тогда для total.p. добавил вместо (int)$this->config->get('config_store_id') на:
    (int)$cid - так грамотней будет считать для каждой категории кол-во товара из кеша, а то на данный момент - всё в массиве ... но так же неизвестно какую оптимальную тут выборку записать:
    $query = $this->db->query($sql);
     
    cereberlum нравится это.
  16. nix

    nix php, MySQL, UNIX, MikroTik ROSAPI

    Регистрация:
    16 янв 2013
    Сообщения:
    1.000
    Симпатии:
    890
    уважаемый, смотрим код, ИД категории добавляется в конец названия!
    тоесть уже давно грамотно написано мной)
    и учитывается все, и язык и стор ИД и категория
     
  17. Baco

    Baco Антихронофаг Команда форума

    Регистрация:
    9 окт 2012
    Сообщения:
    803
    Симпатии:
    399
    Глянул, да, в конце есть по кат. ИД, я чего то пропустил, но в финале, я б ещё добавил при редактировании и добавлении записи в категории в конце метода:

    Код:
    $this->cache->delete('total.p.' .(int)$this->config->get('config_language_id') . '.' . (int)$this->config->get('config_store_id') . '.' . (int)$category_id);
     
  18. nix

    nix php, MySQL, UNIX, MikroTik ROSAPI

    Регистрация:
    16 янв 2013
    Сообщения:
    1.000
    Симпатии:
    890
    итог ето админская часть, с етим спорить не буду, но скажу лиш одно, а что если меняется например урл товара, что кеш чистит старый урл? Хрен там, нужно ручками чистить или модулем...
    Функцию очистки кеша нужно применять к большенству функционалу опенкарта, чего зачастую опускается при разработке модулей... да ток тут еще б переделать библиотеку кеша, чтоб при изменении кешированого елемента не удалялся старый кеш а генерировался из старого образца новый, можна например ето в масив записать и пересобрать потом, или во временый кеш и после пересбора удялять временый, вот тогда ето будет ефективно.

    И речь шла об кешировании только а не допилке всех мелочей.
     
  19. cereberlum

    cereberlum

    Регистрация:
    26 май 2013
    Сообщения:
    756
    Симпатии:
    269
    Заметилась интересная и не очень приятная особенность, в модуле "категории"(стандартном) при входе в категорию колличество товара увеличивается вдвое. Ну скажем на главной видим что в ноутбуках 14 товаров, и это действительно так - переходим в категорию и товаров по-прежнему 14-по-факту, а по цифрам 28 (только в подсчете для модуля"категории". Опытным путем установлено что дело в системном кэше. Есть идеи откуда ноги растут? Спасибо.
     
  20. nix

    nix php, MySQL, UNIX, MikroTik ROSAPI

    Регистрация:
    16 янв 2013
    Сообщения:
    1.000
    Симпатии:
    890
    версия опенкарта (сборки) какая?