За последние 24 часа нас посетили 26489 программистов и 1510 роботов. Сейчас ищут 828 программистов ...

Скрипт вешается

Тема в разделе "PHP для новичков", создана пользователем NerdRage, 8 авг 2016.

  1. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    Есть скрипт просчёта статистики неважно чего, там каждый день просчитывается поочерёдно. И есть юзер со статистикой за 635 дней, которая просчитывается ооочень долго при открытии страницы. Я решил сделать систему кэширования для всего этого. В цикле где просчитываются дни, статистику каждого дня я стал писать в базу. Упрощённый пример:

    Было:
    PHP:
    1. foreach($days as $day) {
    2.     // do stuff
    3.     $out .= 'test';
    4. }
    5. echo $out;
    Стало:
    PHP:
    1. foreach($days as $day) {
    2.    // do stuff
    3.    $stats = 'test';
    4.    $out .= $stats;
    5.    
    6.    // запись кэша, если нужно
    7.    $query = $db->query("SELECT `id` FROM `stats` WHERE `user`=$user AND `day`=$day");
    8.    $row = mysqli_fetch_array($query, MYSQLI_ASSOC);
    9.    if (!$row['id']) {
    10.       $db->query("INSERT INTO `stats` SET `stats`='$stats' WHERE `user`=$user AND `day`=$day");
    11.    }
    12. }
    13. echo $out;
    Итог - страница стала вешаться где-то на 140-м дне, хотя раньше статистика за 635 дней могла просчитаться за раз, хоть и долго прогружалась. Что я сделал не так? Почему вторая запись гораздо нагружает сервер в ынацать раз сильнее, чем первая? Может есть способ выполнять SQL-запросы не в цикле, а собрать их в переменную и выполнить один раз в конце?
     
    #1 NerdRage, 8 авг 2016
    Последнее редактирование: 8 авг 2016
  2. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    Вообще это можно одним запросом заполнить, но только по листингу не понятно, чем тебе можно помочь.
     
  3. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    А как такой запрос составить? Там наверно не получится одним запросом, потому что там могут быть как INSERT, так и UPDATE. В функции есть флаг $cache - если он false, значит кэш надо пересчитать с нуля, то есть сделать UPDATE существующих дней и сделать INSERT недостающих. Выглядит это так сейчас. Как тут вынести запросы за цикл - хз.
    PHP:
    1. foreach($days as $day) {
    2.     // do stuff
    3.     $stats = 'test';
    4.     $out .= $stats;
    5.  
    6.     // запись кэша, если нужно
    7.     $query = $db->query("SELECT `id` FROM `stats` WHERE `user`=$user AND `day`=$day");
    8.     $row = mysqli_fetch_array($query, MYSQLI_ASSOC);
    9.     if (!$row['id']) {
    10.         $db->query("INSERT INTO `stats` SET `stats`='$stats' WHERE `user`=$user AND `day`=$day");
    11.     } else {
    12.         if (!$cache) {
    13.             $db->query("UPDATE `stats` SET `stats`='$stats' WHERE `user`=$user AND `day`=$day");
    14.         }
    15.     }
    16. }
    17. echo $out;
     
  4. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    Ппц...
    REPLACE INTO ... SELECT ... FROM ... WHERE ... - всё что тебе нужно
     
  5. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    @Ganzal А так будет не проще в плане русорсо-потребления?
    PHP:
    1. if (!$cache) {
    2.     $db->query("DELETE FROM `stats` WHERE `user`=$user");
    3. }
    4. foreach($days as $day) {
    5.     // do stuff
    6.     $stats = 'test';
    7.     $out .= $stats;
    8.     // запись кэша, если нужно
    9.     $query = $db->query("SELECT `id` FROM `stats` WHERE `user`=$user AND `day`=$day");
    10.     $row = mysqli_fetch_array($query, MYSQLI_ASSOC);
    11.     if (!$row['id']) {
    12.         $db->query("INSERT INTO `stats` SET `stats`='$stats' WHERE `user`=$user AND `day`=$day");
    13.     }
    14. }
    15. echo $out;

    Я бы хотел сделать что-то такое, но хз можно ли так.
    PHP:
    1. if (!$cache) {
    2.     $db->query("DELETE FROM `stats` WHERE `user`=$user");
    3. }
    4. foreach($days as $day) {
    5.     // do stuff
    6.     $stats = 'test';
    7.     $out .= $stats;
    8.     // запись кэша, если нужно
    9.     $query = $db->query("SELECT `id` FROM `stats` WHERE `user`=$user AND `day`=$day");
    10.     $row = mysqli_fetch_array($query, MYSQLI_ASSOC);
    11.     if (!$row['id']) {
    12.         $sql .= "INSERT INTO `stats` SET `stats`='$stats' WHERE `user`=$user AND `day`=$day ;";
    13.     }
    14. }
    15. $db->query($sql);
    16. echo $out;
     
  6. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    давай с другой стороны зайдем. Что такое ду стаф?
     
  7. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    Не суть, там выполняется километровый код расчёта кучи всего. Цикл довольно нагруженный, но он выполнялся за раз всё-таки. Вешаться стало когда я добавил в цикл вот эти SQL-запросы с записью кэша.
     
  8. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    Ну если тебе не суть, то говнокодь дальше
     
  9. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    @Ganzal Спасибо за помощь, ёпты...
     
  10. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    Да не за что. Тебе пытались подсказать, но тебе не суть. Ты хочешь вот так - делай вот так. Ты делаешь ПРАВИЛЬНО. Устраивает? Подбодрил?
     
  11. mkramer

    mkramer Суперстар
    Команда форума Модератор

    С нами с:
    20 июн 2012
    Сообщения:
    8.598
    Симпатии:
    1.764
    А что такое insert ... where? Ни разу не встречал такого в доке по mysql
     
    askanim и denis01 нравится это.
  12. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    @Ganzal Ты херню несёшь, парень. Я обратился с конкретной проблемой - сократить нажористость SQL-запросов. Вместо этого тебе зачем-то надо копаться в остальном моём коде. Меня интересует, есть ли разница по нагрузки между этими подходами:
    PHP:
    1. $numbs = array(1,2,3);
    2. foreach($numbs as $numb) {
    3.     $db->query("INSERT INTO `test` SET `field1`='example', `field2`=$numb ");
    4. }
    PHP:
    1. $numbs = array(1,2,3);
    2. foreach($numbs as $numb) {
    3.     $sql[] = "('example', $numb)";
    4. }
    5.  
    6. $sql = implode(',', $sql); // = "('example', 1), ('example', 2), ('example', 3)"
    7. $db->query("INSERT INTO `test` (`field1`, `field2`) VALUES $sql ;");
    Если не знаешь, то зачем что-то писать вообще? Чтобы повыпендриваться в разделе "для новичков"?
     
    #12 NerdRage, 8 авг 2016
    Последнее редактирование: 8 авг 2016
  13. Ganzal

    Ganzal Суперстар
    Команда форума Модератор

    С нами с:
    15 мар 2007
    Сообщения:
    9.893
    Симпатии:
    965
    Если ты хочешь спросить "быстрее выполнить один запрос вставки или миллион" то да, быстрее будет работать один или несколько запросов вставки с пакетами данных.
    Мой вопрос про ду стаф был направлен на попытку выяснить нельзя ли вообще один запрос выполнить с перекачкой из одной таблицы, агрегации и вставки-обновления другой.
    Голову включи перед тем как отвечать.
     
  14. MouseZver

    MouseZver Суперстар

    С нами с:
    1 апр 2013
    Сообщения:
    7.814
    Симпатии:
    1.332
    Адрес:
    Лень
    @NerdRage 2 способ более сообразителен, нежели в цикле насиловать запросами БД. По сути ничего особого нет, что в первом и во 2ом.

    Наверное в PDO было бы легче без сбора данных эксешить:
    PHP:
    1. $numbs = array ( 1, 2, 3 );
    2. $PDO = PDO::prepare( "INSERT INTO test ( 'field1',  'field2' ) VALUES ( ?, ? )" );
    3. foreach ( $numbs AS $numb )
    4. {
    5.     $PDO -> execute( array ( 'example', $numb ) );
    6. }
     
    #14 MouseZver, 8 авг 2016
    Последнее редактирование: 8 авг 2016
  15. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    Выясняются новые подробности. Оказывается, у меня на локалке скрипт вешался, потому что у меня стоит PHP 7. А на хостинге он при этом нормально отрабатывает, там PHP 5.3. Вот эти директивы там и там одинаковые:
    Код (Text):
    1. max_execution_time = 60
    2. max_input_time = -1
    3. max_input_nesting_level = 64
    4. max_input_vars = 3000
    5. memory_limit = 128M
    Вот phpinfo() с хостинга: http://185.5.248.25/hosting.htm
    Вот с локалки: http://185.5.248.25/local.htm
     
  16. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    Короче я перешёл тестить на локалку - под OpenServer вешается, а под Denwer нет. При этом версию PHP я поставил идентичную, с одинаковым php.ini - 5.3.29. Даже не знаю из-за чего ещё это может быть. Версия mysql там и там 5.5. Версии апача только отличаются - 2.2 в OpenServer и 2.0 в Denwer.
     
  17. askanim

    askanim Старожил

    С нами с:
    7 апр 2016
    Сообщения:
    2.201
    Симпатии:
    166
    Адрес:
    GABRIEL
    @NerdRage бррр.... код вешает не правильный алгоритм действий, дебаж!
    --- Добавлено ---
    вот этот чувак знает больше чем я с тобой вместе взятый и ещё таких по три, он конечно любитель потроллить, но он попытался понять, что у тебя в коде происходит. Ты как себе представляешь помощь тебе, без понимания логики твоего кода? Я тебе так скажу твой тут супер крутой алгоритм поверь, который ты там прячешь, нахер не кому не усрался. Знаешь сколько таких крутых в интернете? А помочь без полного понимания логики твоего кода не возможно, можно лишь гадать и ванговать, ну и на синтаксис посмотреть и сказать где у тебя там ошибка, но тоже может сделать и ide и debug php.
    --- Добавлено ---

    @NerdRageИ это не истерика, это факт хочешь помощи, расскажи логику своей программы и тебе помогут скажут где что не так и как лучше сделать, иначе это как гадать на кофейной гущи.
     
  18. mahmuzar

    mahmuzar Старожил

    С нами с:
    6 апр 2012
    Сообщения:
    4.631
    Симпатии:
    425
    Адрес:
    РД, г. Махачкала.
    :D
    --- Добавлено ---
    простите, не сдержался!
     
    askanim нравится это.
  19. askanim

    askanim Старожил

    С нами с:
    7 апр 2016
    Сообщения:
    2.201
    Симпатии:
    166
    Адрес:
    GABRIEL
    @mahmuzar :D

    ну я не говорил не разу что мои скрипты крутые, и я их не шкерю :D
     
  20. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    Свой код я не шарю не потому что я боюсь, что мою мега-разработку стырят, а потому что он километровый, он даже в сообщение на форуме вряд ли влезет - кому-то охота вчитываться в такое? Я специально упростил пример по максимуму, чтобы не выносить людям мозг. Вместо этого мозг начали выносить мне. Короче, если свести всё к изменениям, которые я делал, то тут два варианта:
    1. Добавил запись в БД в цикле.
    2. Переделал запросы в БД с MySQL на MySQLi. То есть, теперь формат такой:
    PHP:
    1. function query($query) {
    2.     return mysqli_query($this->db, $query);
    3. }
    Может в этом причина кстати? Может устаревший формат mysql_query быстрее?
     
  21. askanim

    askanim Старожил

    С нами с:
    7 апр 2016
    Сообщения:
    2.201
    Симпатии:
    166
    Адрес:
    GABRIEL
  22. mr.akv

    mr.akv Активный пользователь

    С нами с:
    31 мар 2015
    Сообщения:
    1.604
    Симпатии:
    206
    слушай, а ты совсем эскейпы не используешь? или я пропустил что
     
  23. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    Использую, для эскейпов просто отдельная функция. Не помню зачем я так сделал. Но теперь уже, работает - не трогай. :D
     
  24. NerdRage

    NerdRage Активный пользователь

    С нами с:
    6 июл 2016
    Сообщения:
    439
    Симпатии:
    42
    Вчера выложил систему на боевой, ночью просчитывался кэш - 413 тысяч попугаев вместо обычных 726. И это он ещё на половину не просчитался. :D
    [​IMG]
    Страницы всё-таки стали открываться на много медленнее, когда чтение из кэша выключено. А должно быть тоже самое. Есть какой-то способ померить у себя на локалке нагрузку на MySQL, количество запросов и всё такое? У меня есть старая версия скрипта, которая работала быстро и новая версия, которая работает медленно. Хочу сравнить по нагрузке, и методом перебора выяснить что вызывает затупливание. Я только знаю как посчитать время выполнения PHP-скрипта.
     
  25. denis01

    denis01 Суперстар
    Команда форума Модератор

    С нами с:
    9 дек 2014
    Сообщения:
    12.227
    Симпатии:
    1.714
    Адрес:
    Молдова, г.Кишинёв
    Замерь куски кода, так найдёшь которые долго работают.
    Ещё есть xdebug там он может создавать отчёт потом его можно открыть и посмотреть в графическом виде например, что дольше всего выполнялось.
     
    askanim нравится это.