Как ускорить выборку?

Всем привет!

Работаю над магазином с 50 000 товаров. Встал вопрос о скорости выборки.
Использую msProducts с prepareSnippet, но результат не устраивает.
Вот лог:
0.0000420: xPDO query object created
0.0002220: leftJoined msProductData as Data
0.0001690: leftJoined msVendor as Vendor
0.0000870: Added selection of msProduct: SQL_CALC_FOUND_ROWS `id`, `type`, `contentType`, `pagetitle`, `longtitle`, `description`, `alias`, `link_attributes`, `published`, `pub_date`, `unpub_date`, `parent`, `isfolder`, `introtext`, `richtext`, `template`, `menuindex`, `searchable`, `cacheable`, `createdby`, `createdon`, `editedby`, `editedon`, `deleted`, `deletedon`, `deletedby`, `publishedon`, `publishedby`, `menutitle`, `donthit`, `privateweb`, `privatemgr`, `content_dispo`, `hidemenu`, `class_key`, `context_key`, `content_type`, `uri`, `uri_override`, `hide_children_in_tree`, `show_in_tree`, `properties`
0.0000348: Added selection of msProductData: `article`, `price`, `old_price`, `weight`, `image`, `thumb`, `vendor`, `made_in`, `new`, `popular`, `favorite`, `tags`, `color`, `size`, `source`, `profile`, `height`, `diametr`, `speed_index`, `season`, `type_tires`, `shipy`, `gruzo`, `run_flat`, `available`, `period`, `number_circle`, `pcd1`, `et`, `dia`, `marka_auto`
0.0000250: Added selection of msVendor: `name` AS `vendor.name`, `resource` AS `vendor.resource`, `country` AS `vendor.country`, `logo` AS `vendor.logo`, `address` AS `vendor.address`, `phone` AS `vendor.phone`, `fax` AS `vendor.fax`, `email` AS `vendor.email`, `description` AS `vendor.description`, `properties` AS `vendor.properties`
0.0236011: Processed additional conditions
0.0395770: Added where condition: class_key=msProduct, Data.available:>=0, parent:==5, Data.price:>==0, msProduct.id:IN(50135,---много-много---,46135), msProduct.published=1, msProduct.deleted=0, msProduct.context_key=web
0.0001009: Sorted by Data.price, ASC
0.0000069: Limited to 30, offset 
0.0031209: SQL prepared "SELECT SQL_CALC_FOUND_ROWS `msProduct`.`id`, `msProduct`.`type`, `msProduct`.`contentType`, `msProduct`.`pagetitle`, `msProduct`.`longtitle`, `msProduct`.`description`, `msProduct`.`alias`, `msProduct`.`link_attributes`, `msProduct`.`published`, `msProduct`.`pub_date`, `msProduct`.`unpub_date`, `msProduct`.`parent`, `msProduct`.`isfolder`, `msProduct`.`introtext`, `msProduct`.`richtext`, `msProduct`.`template`, `msProduct`.`menuindex`, `msProduct`.`searchable`, `msProduct`.`cacheable`, `msProduct`.`createdby`, `msProduct`.`createdon`, `msProduct`.`editedby`, `msProduct`.`editedon`, `msProduct`.`deleted`, `msProduct`.`deletedon`, `msProduct`.`deletedby`, `msProduct`.`publishedon`, `msProduct`.`publishedby`, `msProduct`.`menutitle`, `msProduct`.`donthit`, `msProduct`.`privateweb`, `msProduct`.`privatemgr`, `msProduct`.`content_dispo`, `msProduct`.`hidemenu`, `msProduct`.`class_key`, `msProduct`.`context_key`, `msProduct`.`content_type`, `msProduct`.`uri`, `msProduct`.`uri_override`, `msProduct`.`hide_children_in_tree`, `msProduct`.`show_in_tree`, `msProduct`.`properties`, `Data`.`article`, `Data`.`price`, `Data`.`old_price`, `Data`.`weight`, `Data`.`image`, `Data`.`thumb`, `Data`.`vendor`, `Data`.`made_in`, `Data`.`new`, `Data`.`popular`, `Data`.`favorite`, `Data`.`tags`, `Data`.`color`, `Data`.`size`, `Data`.`source`, `Data`.`profile`, `Data`.`height`, `Data`.`diametr`, `Data`.`speed_index`, `Data`.`season`, `Data`.`type_tires`, `Data`.`shipy`, `Data`.`gruzo`, `Data`.`run_flat`, `Data`.`available`, `Data`.`period`, `Data`.`number_circle`, `Data`.`pcd1`, `Data`.`et`, `Data`.`dia`, `Data`.`marka_auto`, `Vendor`.`name` AS `vendor.name`, `Vendor`.`resource` AS `vendor.resource`, `Vendor`.`country` AS `vendor.country`, `Vendor`.`logo` AS `vendor.logo`, `Vendor`.`address` AS `vendor.address`, `Vendor`.`phone` AS `vendor.phone`, `Vendor`.`fax` AS `vendor.fax`, `Vendor`.`email` AS `vendor.email`, `Vendor`.`description` AS `vendor.description`, `Vendor`.`properties` AS `vendor.properties` FROM `modx_site_content` AS `msProduct` LEFT JOIN `modx_ms2_products` `Data` ON `msProduct`.`id`=`Data`.`id` LEFT JOIN `modx_ms2_vendors` `Vendor` ON `Data`.`vendor`=`Vendor`.`id` WHERE  ( `msProduct`.`class_key` = 'msProduct' AND `Data`.`available` > '0' AND `msProduct`.`parent` = 5 AND `Data`.`price` >= '0' AND `msProduct`.`id` IN (50135,---много-много---,46135) AND `msProduct`.`published` = 1 AND `msProduct`.`deleted` = 0 AND `msProduct`.`context_key` = 'web' )  ORDER BY Data.price ASC LIMIT 30 "
0.9103260: SQL executed
0.0002890: Total rows: 16527
0.0017149: Rows fetched
0.0015352: Returning raw data
0.0023639: Checked the active modifiers
0.0088351: Loaded chunk "product_in_cat_tires"
0.1645899: Returning processed chunks
1.1339228: Total time
64 487 424: Memory usage
Как видите, общее время выборки 16527 товаров составляет 1,13 с. Только время SQL запроса 0,91 с!
Парсинг чанка с помощью &prepareSnippet удалось свести к минимуму (было ещё выше), но всё равно это много 0,16 с!

В чанке нет ни одного условия, кроме спец. комментариев для «быстрых плейсхолдеров»:
<!--parse_old_price [[+old_price]] руб.-->
<!--parse_old_price2 <span class="sale-box"><span class="sale-label">Sale!</span></span>-->
<!--parse_kol_vo 4-->
<!--parse_!kol_vo 1-->
<!--parse_thumb [[+thumb]]-->
<!--parse_!thumb /assets/img/no_photo.png-->
Кто что может подсказать по данному поводу? Как убыстрить выборку? Буду очень благодарен за конструктивные советы и критику))

Да, и ещё, заметил, если в выборке не будет такой простыни msProduct.id:IN(50135,---много-много---,46135) в условии WHERE, а будет, например, msProduct.id !== 0, то выборка проходит значительно быстрее, но всё же для фильтра необходимо условие msProduct.id:IN().
Андрей
06 апреля 2015, 18:18
modx.pro
2
3 314
0

Комментарии: 27

Василий Наумкин
06 апреля 2015, 21:47
0
Если не устраивает работа стандартного сниппета — пиши свой сниппет, с более оптимальным SQL запросом.

Начать стоит с EXPLAIN SELECT SQL_NO_CACHE SQL_CALC_FOUND_ROWS `msProduct`.`id`…
    Андрей
    Андрей
    06 апреля 2015, 22:00
    0
    Василий, я так предполагаю, что msProduct.id:IN() в условие для msProducts подставляет mFilter2, поскольку в данном случае они работают в паре?
      Василий Наумкин
      06 апреля 2015, 22:23
      0
      Да, скорее всего.

      Фильтр подбирает подходящие ресурсы и отдаёт их id в msProducts, а тот уже выводит.
        Андрей
        Андрей
        06 апреля 2015, 22:30
        0
        Да, mFilter подставляет. Поменял в вызове mFilter2
        &element=`msProducts`
        на
        &element=`pdoResources`
        &leftJoin=`{
                "Data": {
                 "class": "msProductData",
                 "on": "msProduct.id = Data.id"
                }
            }`
        чтобы лишнее не инджойнить, всё равно время запроса большое из-за длинного msProduct.id:IN(). Значит нужно что-то с фильтром делать(((
          Василий Наумкин
          06 апреля 2015, 22:37
          0
          Делай, конечно, раз не устраивает фильтрация 16 тысяч товаров за полторы секунды.
            Андрей
            Андрей
            06 апреля 2015, 22:43
            0
            Фишка в том, что при Ajax пагинации эти 1,5 с. выливаются во все 5-7 с! А если к странице обращаться GET запросом, то те же самые 1,5 с. Вот я и озадачился)
              Андрей
              Андрей
              06 апреля 2015, 22:48
              0
              Даже больше при Ajax — показало 16.77 с., заоблачное число.
              Андрей
              Андрей
              06 апреля 2015, 23:01
              0
              Василий, подскажи, пожалуйста, что можно с Ajax пагинацией сделать? Удобная вещь, не хотелось бы от неё отказываться. Почему с ней так долго запрос в базу идёт?
                Василий Наумкин
                07 апреля 2015, 05:16
                0
                Не знаю
                  Алексей
                  07 апреля 2015, 06:37
                  0
                  на последней версии msearch2 есть такое. отключи suggestions — будет все гораздо быстрее. В версии msearch2 1.0.2pl все фильтруется гораздо быстрее чем в нынешних, даже с включенным suggestions. (субъективное мнение, конечно)
                    Василий Наумкин
                    07 апреля 2015, 07:46
                    0
                    Ничего, что работа с suggestions не менялась с версии 0.6.0-beta? Это объективный факт.

                    У mFilter2 есть свой лог, где показано, сколько времени генерируются предсказания результатов. Без них, конечно, всегда быстрее.
                      Андрей
                      Андрей
                      07 апреля 2015, 10:15
                      0
                      Это понятно, на таких объёмах у меня всё это отключено. Да и к самому фильтру у меня нареканий нет — фильтрует довольно быстро и ajax-ово обновляется. У меня засада именно с самой пагинацией на всём объеме выборки. Как только отфильтруешь товары, чтобы в выдаче было менее 5000 результатов, то и пагинация начинает быстро (относительно) работать.
                        Василий Наумкин
                        07 апреля 2015, 13:47
                        0
                        Ну вот ты лог с обычной загрузки страницы показал. Что мешает показать лог работы сниппетов при загрузке через ajax?

                        Они тоже выводятся на страницу. Может у тебя там сторонний какой плагин реагирует на ajax запрос, может еще что-то, нужно разбираться.

                        Сам по себе ajax должен быть быстрее, потому что грузится не целая страница, а только нужный кусок контента. Его для того и придумали — чтобы ускорять.
                          Андрей
                          Андрей
                          07 апреля 2015, 14:17
                          0
                          На странице только один лог от msProducts, который вызывается в сниппете mFilter2 параметром showLog:
                          <pre class="msProductsLog">...дальше лог, который в топике
                            Василий Наумкин
                            07 апреля 2015, 14:22
                            0
                            На странице 2 лога, один от mFilter2, а второй от msProducts


                            И они оба обновляются при работе через ajax. Если там те же полторы секунды, то проблема явно не в этих дополнениях.
                            Андрей
                            Андрей
                            07 апреля 2015, 14:39
                            0
                            Василий, у меня лог один. Версия mSearch2 ещё древняя 1.1.0-pl, хотя, наверно, логирование у тебя было предусмотрено уже в первой версии. Сайт перешёл от прежнего владельца, поэтому сейчас стараюсь оптимизировать его работу.

                            Никаких доп. сниппетов нет. Но вот при таком условии:
                            &where=`{"Data.available:>":0,"parent:=":5,"Data.price:>=":0}`
                            и при стандартном вызове — тормозит безбожно на полной выборке 16000+ при пагинации, а при таком условии:
                            &where=`{"Data.available:>":0,"parent:=":5,"Data.price:>=":0,"msProduct.id:!=":0}`
                            всё работает как часы, те же 1,5 с., но только в условии WHERE SQL запроса получается нет длинной простыни из msProduct.id:IN().
                            Василий Наумкин
                            07 апреля 2015, 15:05
                            0
                            Ну а как еще можно указать сниппету список ресурсов, которые прошли фильтр?

                            Я не знаю таких способов, только простыня из подходящих id.
                            Андрей
                            Андрей
                            07 апреля 2015, 17:21
                            0
                            В общем поборол путём добавления в сниппет msProducts принудительного параметра
                            $where['msProduct.id:!='] = 0;
                            при условии работы с полной выборкой из категории (т.е., если не применены никакие фильтры).
                            А если хоть один фильтр применён, то тут в любом случае нужен оператор IN во WHERE, но и выборка меньше в этом случае, и в итоге во всех случаях общее время не превышает 1,5 с.
                            Василий Наумкин
                            07 апреля 2015, 18:14
                            0
                            Ну и хорошо, будем считать, что вопрос решен.
                            Андрей
                            Андрей
                            09 апреля 2015, 01:56
                            0
                            Василий, есть ещё вопрос.
                            Когда для каталога товаров указаны разные чанки для первого, последнего и между ними товаров, то всегда показывает время загрузки чанков по нарастающей от первого до последнего, так:
                            0.0045290: Loaded chunk "cat.first.goods"
                            0.0480869: Loaded chunk "cat.middle.goods"
                            0.2051361: Loaded chunk "cat.last.goods"
                            Эти чанки идентичны с точки зрения используемых плейсхолдеров. Почему так может быть?
                            Василий Наумкин
                            09 апреля 2015, 06:40
                            0
                            Потому что эти чанки нужны в разное время.

                            Цикл перебирает массив результатов и запрашивает чанки, когда они нужны. То есть, это не время загрузки чанка, а время, которое прошло с предыдущего этапа работы.

                            Первая строка, середина массива, и последняя строка. Можно сказать, что весь массив был оформлен за 0.004 + 0.048 + 0.205 сек.
                            Андрей
                            Андрей
                            09 апреля 2015, 14:24
                            0
                            Всё понятно, спасибо.
                    Wassi Wassinen
                    07 апреля 2015, 08:57
                    0
                    Если есть подсчет результатов — отключи. Судя по замерам и опросам — от этого ни горячо, ни холодно на большинстве проектов.
                      Пашок
                      Пашок
                      07 апреля 2015, 14:32
                      0
                      К сожалению только со включенным параметром &suggestions чекбоксам присваивается класс disabled. А это по сути очень удобно, запрещать пользователю выбор вариантов, по которым заведомо пусто.
        Виталий Киреев
        09 апреля 2015, 05:47
        0
        Если на `Data`.`available` нет индекса, то не помешал бы.
          Андрей
          Андрей
          09 апреля 2015, 14:23
          0
          Есть индекс, спасибо)
          Авторизуйтесь или зарегистрируйтесь, чтобы оставлять комментарии.
          27