We launched new forums in March 2019—join us there. In a hurry for help with your website? Get Help Now!
    • 30557
    • 11 Posts
    Добрый день. Новичок в MODX. Нужен совет профессионалов в такой ситуации.
    Есть MySQL база. Есть сниппет, который выполняет некоторый SQL-запрос и сохраняет данные в XML формате.
    Также, на страничках саЕсть много Запрос достаточно б
      • 30557
      • 11 Posts
      Добрый день. Новичок в MODX. Нужен совет профессионалов в такой ситуации.

      Есть MySQL база.
      Есть "большой" сниппет, который выполняет некоторый SQL-запрос и сохраняет данные в XML формате.
      Также, на страничках сайта есть много "маленьких" сниппетов, которые парсят XML и растаскивают содержимое по разным частям страницы.
      Сейчас маленькие сниппеты вызывают внутри себя большой сниппет. То есть, получается, что один и тот же запрос к MySQL выполняется столько раз, сколько маленьких сниппетов используется на странице.
      А хочется, чтобы большой сниппет вызывался только один раз, результат (который, есть XML) сохранялся в каком-то кэше. И маленькие сниппеты брали данные оттуда. Разумеется, кэш вызовов "большого" сниппета должен обновляться, с какой-то периодичностью. Но предполагается, что пока формируется одна страница, даные в кэше не успели измениться.

      Вопрос: Каким образом можно оптимизировать скорость работы сниппетов MODX, спроектированных по такой схеме?

      Первый случай - сайт и MySQL лежат на одном хосте. Тогда формировать лишних кэш-файлов не хочется (на дисковых операциях потеряется скорость). Быть может, профессионал подскажут, каким образом следует оптимизировать настройки MySQL, дабы все это кешировалось самим MySQL.

      Второй вариант - наоборот, база и php лежат на разных хостех. Т.е. выполнение и передача результатов громоздкого запроса отнимает достаточно много времени. В этом случае надо сделать кэширование на стороне php. Подскажите, пожалуйста, как это можно сделать. Есть ли в MODX какие-то стандартные настройки, управляющие кешированием результатов работы отдельных сниппетов.

      Буду крайне признателен за любые ссылки и образцы кода.
        • 785
        • 2,113 Posts
        Quote from: trash0000 at Jun 25, 2010, 04:00 AM

        Тогда формировать лишних кэш-файлов не хочется (на дисковых операциях потеряется скорость). Быть может, профессионал подскажут, каким образом следует оптимизировать настройки MySQL, дабы все это кешировалось самим MySQL.
        На дисковых операциях потерь практически нет, да и вообще-то база MySQL тоже находится в файле на диске. Проще всего выполнять по cron «большой» запрос и сохранять результаты в файле XML, откуда его будут читать другие сниппеты по мере необходимости. Этот вариант самый простой и наиболее эффективный.
          Создание сайтов на MODx, поддержка сайтов, поисковая оптимизация, программирование, копирайтинг
          Статьи о MODx, регулярно новые публикации
          • 30557
          • 11 Posts
          На дисковых операциях потерь практически нет, да и вообще-то база MySQL тоже находится в файле на диске.
          На дисковых операциях, времени на запись файла тратится в 2-20 раз больше, чем на чтение (т.к. обычно у хостера стоит какой-нибудь рейд, делающий ассиметричным скоростные показатели чтения/записи). При этом, физически, диски на которых стоит MySQL и диски, на которые PHP кладет какие-то временные файлы - могут быть совершенно разными физическими дисками двух разных хостов. Добавляем временные затраты на организацию файлов кэша, время на поиск нужного файла с нужной информацией в кеше. И получаем, неутешительный вывод:
          что выполнить заново MySQL запрос будет эффективней, чем пытаться найти нужный кешированный результат. Даже если удастся спроектировать оч-оч эффективный кэш.
          Проще всего выполнять по cron «большой» запрос и сохранять результаты в файле XML, откуда его будут читать другие сниппеты по мере необходимости. Этот вариант самый простой и наиболее эффективный.
          Вопрос: Поделитесь, пожалуйста, ссылкой разработку подобного рода.

          Крона нет. Причем, нет ни на MySQL ни на PHP хостах. Запросы выполняются в момент, когда начинает свою работу "большой" сниппет на странице.
          Вопрос: Есть ли в MOXD стандартные средства управления кешированием результатов работы отдельных сниппетов?

          Получается, единственным эффективным способом будет такая настройка SQL, при которой результаты хранятся некоторое время во внутреннем кеше запросов. Это время дб сравнимо со средним временем формирования страницы.
          Вопрос:Подскажите, пожалуйста, какие параметры MySQL, влияют на время сохранения данных во внутреннем кеше? Могу ли я отрегулировать их в консоли phpmysqladmin? (Или остается только писать письма хостеру MySQL и надеяться, что у него выбраны оптимальные значения.)
            • 785
            • 2,113 Posts
            О каких вообще значениях времени идет речь? Если страница генерируется примерно за 0,1 секунду, время на чтение еще одного файла XML составит настолько малую величину, что ею можно пренебречь. И время записи в файл вряд ли будет больше времени обращения к MySQL, если запрос более-менее сложный (для простых запросов ничего кэшировать и не требуется).
            В действительности предложенную схему (сохранение в файле XML) я использую достаточно часто, как для сохранения данных, полученных из внешних источников (например, курсы валют или прогноз погоды), так и для кэширования сложных запросов к базе (база из 2 миллионов записей). Вместе с тем для базы некоторые сложные запросы я выполняю из программ, вызываемых по крону, и сохраняю результаты в специальных таблицах этой же базы, так что во время генерации страниц сайта все запросы выполняются быстро.
            То, что нет крона – очень большой недостаток. Как бы эффективно не было кэширование, все равно первый раз страница будет генерироваться долго. В принципе можно добавить дешевый хостинг с кроном, который будет только выполнять регулярно задачи по кэшированию, вызывая соответствующие скрипты на основном хостинге.
            Специальных стандартных средств кэширования результатов сниппетов нет, хотя на форуме выкладывалась такая разработка, но лично я не уверен в целесообразности ее использования.

            ДОБАВЛЕНИЕ. Понял, что мы говорим немного о разных вещах – я подразумевал, что большой запрос предназначен для разных страниц, а оказывается он используется в нескольких сниппетах при генерации одной страницы. Ну в этом случае все намного проще – результат хранить в глобальной переменной и использовать его во всех сниппетах. То есть проверять, если нужная переменная имеется, брать данные из нее, а если нет – обращаться к базе. Кстати, лично я не стал бы формировать XML и обратно его раскодировать, а хранил бы информацию просто в массиве или объекте PHP.
              Создание сайтов на MODx, поддержка сайтов, поисковая оптимизация, программирование, копирайтинг
              Статьи о MODx, регулярно новые публикации
              • 9085
              • 630 Posts
              1 по реализации - думаю, вполне подойдет вариант:

              сохраняем результат обработки в переменную, её при помощи var_dump()/var_export() записываем в файл my_cache.php. При необходимости обратиться - просто инклудим файл {include ’my_cache.php’} (к вопросу о потерях времени - сколько файлов "инклудится" при выводе одной страницы?)

              2. А вообще, можно обходиться без крона =) боты поисковые по ночам ходят.. если не ходят - можно пригласить (сапу ту же smiley) время засекаем по времени создания временного файла.

              по "нет крона" - возможно, рассмотреть вариант перехода на другой хостинг.

              3.
              На дисковых операциях, времени на запись файла тратится в 2-20 раз больше, чем на чтение
              вроде как читать чаще надо?
              (т.к. обычно у хостера стоит какой-нибудь рейд, делающий ассиметричным скоростные показатели чтения/записи). При этом, физически, диски на которых стоит MySQL и диски, на которые PHP кладет какие-то временные файлы - могут быть совершенно разными физическими дисками двух разных хостов. Добавляем временные затраты на организацию файлов кэша, время на поиск нужного файла с нужной информацией в кеше. И получаем, неутешительный вывод:
              что выполнить заново MySQL запрос будет эффективней,

              шашечки? или ехать? Теоретики, блин )))
              Интересно, а нафик тогда вообще кэширование придумали?))) %)

              А вообще, больше практики:
              <?php
              $t1 = time(); //micro?
              for ($i=1; $i<=10000; $i++) {
              // variant 1
               fopen(); fread(); fclose(); //
              // variant 2
               mysql_connect();mysql_select_db();mysql_query();
               while(mysql_fetch_row($result)); // don't forget!!!
              }
              $t2 = time();
              echo $t2-$t1;
              ?>


                Правильно заданный вопрос повышает вероятность получения нужного ответа.
                Что-то не работает? Загляните в лог ошибок!
                Есть проблема? "Локализуйте" её!
                Ответы на большинство вопросов по MODx | MODx RTFM | API Evo Revo | галопом по Европам Азиям smiley wikibooks(ru) Ditto(en) | Wayfinder e-book | Заметки и о MODx
                • 30557
                • 11 Posts
                Кстати, лично я не стал бы формировать XML и обратно его раскодировать, а хранил бы информацию просто в массиве или объекте PHP.
                Массив не получается, ибо не совсем понятно, какие там дб индексы. Основные операции по такой XML - горизонтальная выборка по одной строке, или вертикальна выборка по столбцу (с расстановкой обрамляющих тегов). Задача решается одним XSLT-преобразованием (которое храню в чанке). Поэтому формат XML для меня в самый раз.
                Понял, что мы говорим немного о разных вещах – я подразумевал, что большой запрос предназначен для разных страниц, а оказывается он используется в нескольких сниппетах при генерации одной страницы. Ну в этом случае все намного проще – результат хранить в глобальной переменной и использовать его во всех сниппетах. То есть проверять, если нужная переменная имеется, брать данные из нее, а если нет – обращаться к базе.
                Спасибо. Этот вариант мне понравился. Можете набросать код для такого модельного случая:
                Большой сниппет
                bigsnippet=
                <?php
                //bla-bla-bla...
                echo "Hello user: ".rand(1,9)." Current time is: ".date("d.m.y h.i.s");
                ?>
                

                И два маленьких сниппета
                mini1=
                <?php
                echo substr ($modx->runSnippet('bigsnippet'r,1, 14);
                

                mini2=
                <?php
                echo substr ($modx->runSnippet('bigsnippet',15);
                

                Страница=
                [!mini1!]
                <br>
                [!mini2!]
                

                Сейчас каждый маленький сниппет делает вызов большого.
                А каким образом можно переделать архитектуру приложения, чтобы большой сниппет не вызывался только 1 раз, а не столько, сколько маленьких сниппетов у меня на странице?
                Пример=?
                (измененные bigsnippet mini1 mini2 код страницы)


                Как сделать, чтобы глобальная переменная была доступна только в одной "микросессии генерации одной страницы"?
                То есть инициализироваться "большим" сниппетом где-то вверху страницы. И использоваться маленькими сниппетами в середине страницы?
                А что произойдет, если большой сниппет "не успевает" к моменту вызова маленького сниппета, т.е. правда ли, что сниппеты на странице выполняются в той последовательности, как написаны (а мб асинхронно)?
                Как сделать, чтобы при другом запросе страницы большой сниппет вычислил переменную заново?
                В общем, слишком много вопросов. Возможно, Ваш код поможет мне понять, что к чему.

                  • 30557
                  • 11 Posts
                  Или давайте отвлечемся от MODX-термина "сниппет".
                  Переформулирую задачу так:
                  Есть медленная процедура на php, которая возвращает большую XML-строку.
                  Есть много других процедур, которые используют ее вызовы.
                  Как средствами php организовать кеширование вызовов медленной процедуры внутри одного GET/POST запроса страницы?

                  Уж, простите, за оффтоп, и за средний уровень познаний в php...
                    • 785
                    • 2,113 Posts
                    Quote from: trash0000 at Jun 26, 2010, 03:03 AM

                    Как средствами php организовать кеширование вызовов медленной процедуры внутри одного GET/POST запроса страницы?
                    Ну проще всего поставить include_once() и в файле хранить код, который будет выполняться только один раз. Если же вопрос в том, чтобы избежать лишних обращений к базе, то можно написать что-то такое:
                    if (!isset($GLOBALS['MyXMLObject'])) {
                    	// Запрос к базе и запись результатов в $MyXMLObject
                    	$GLOBALS['MyXMLObject']=$MyXMLObject;
                    }
                    // использование в программе $GLOBALS['MyXMLObject']
                      Создание сайтов на MODx, поддержка сайтов, поисковая оптимизация, программирование, копирайтинг
                      Статьи о MODx, регулярно новые публикации
                      • 30557
                      • 11 Posts
                      Почитал Ваши ответы... еще раз оценил ситуацию... Не совсем уж так все просто.
                      В самом деле, если бы большой сниппет выполняется без параметров - то можно кешировать результат в глобальную переменную с именем равным имени сниппета.
                      Но ситуация такова, что сниппет выполняется с параметрами (которые, суть, различные варианты условия where в sql-запросе). Так что получается совсем не просто кешировать вызовы одного того же сниппета с резными параметрами. Уж очень много наплодится переменных в $GLOBALS.
                      Еще, оказывается, бывают хостинги, c отключенной опцией register globals. То есть, получаем риск написать php-код, непереносимый на другой хостинг.
                      Но, как бы то ни было, резюмирую решение, навеянное Вашими ответами:
                      1)Решение кешировать или не кешировать результаты работы большого сниппета для каждого его вызова из маленького сниппета принимается идивидуально для каждого случая.
                      2)В каждый маленьком сниппет передаеюся дополнительный параметр: &UseCacheVar=’GlobalVaName’. Ели переменная неинициализирована результат не кешируется. Если инициализирована, то маленький сниппет выполняет следующие действия.
                      3)Проверяет опцию register globals. Если запрещено - то работаем как раньше.
                      4)Если register globals разрешено, то проверяется существование глобальной переменной с именем, переданным в параметре &UseCacheVar=’GlobalVaName’. Если переменная существует, то вместо выполнения SQL-запроса читается ее значение.
                      5)Если переменная не существует, то выполняется запрос, его значение сохраняется в глобальную переменную с таким именем.
                      6)Далее результат обрабатывается, как ранее.
                      Вот пример сниппета mini1, переписанного с учетом данных требований:
                      В сниппете анализируется параметр &UseCacheVar, в котором передается имя глобальной переменной - кеша, который надо проверить.
                      mini1=
                      <&php
                      ( isset($UseCacheVar) ) ? $UseCacheVar: $UseCacheVar= "";
                      if(ini_get('register_globals')!=1 || $UseCacheVar==""):
                      //если запрещено register globals, или не используем кэширование, то все как раньше
                        echo substr($modx->runSnippet('bigsnippet'),1, 14);
                        return;
                      endif;
                      if (isset($GLOBALS[$UseCacheVar])):
                      //если есть в кэше - читаем оттуда
                      	$str=$GLOBALS[$UseCacheVar'];
                        echo substr ($str,1, 14);
                      return;
                      else:
                      // если нет в кеше - то сначала вычисляем и кладем
                      $str=$modx->runSnippet('bigsnippet');
                      $GLOBALS[$UseCacheVar']=$str;
                      //потом, как обычно
                        echo substr ($str,1, 14);
                      endif;
                      ?>

                      Всем ответившим большое спасибо.