phfaster
@phfaster
Прогрессивный веб-разработчик

Почему MySQL не сразу фиксирует результат UPDATE?

Добрейший день.

Делаю загрузку аудиофайлов. Также есть возможность сортировать их (drag&drop). У каждой строки (аудиофайла) в таблице MySQL есть столбец "position", его я у каждого загруженного файла устанавливаю на 1 больше, чем у предыдущего. Делаю я это прибавлением к счетчику, который находится в таблице альбомов (собственно в строку альбома, в который добавляется аудиозапись). После добавления записи в таблицу аудиофайлов, я обновляю счетчик в альбоме (+1) и PHP-скрипт завершается.
JS отправляет следующий запрос со следующим файлом, но увы, MySQL почему-то не успела обновить счетчик в альбоме и там до сих пор старое количество аудиозаписей, получается что каждые две аудиозаписи идут с одинаковой позицией. На второй аудиозаписи MySQL, благо, удосуживается зафиксировать обновление.

Ajax-запросы точно идут по порядку, друг друга не перекрывая. Т.е. первый файл загрузился - только потом начинается загрузка второго. В этом я уверен. Вот интересно, почему PHP завершает скрипт до того, как MySQL закончит свои дела? Может есть какая-то функция закрытия запроса и процесс будет висеть пока MySQL не закончит?

Заранее большое спасибо:)

Кому интересно решение, то вот код до решения:
if($id and $title and $AudioFile) {
    $id = trim($id);
    $title = trim($title);

    if($id !== '' and $title !== '') {
        // Передаём данные формы
        // Проверяем загружен ли файл
        if (is_uploaded_file($AudioFile["tmp_name"])) {
            if ($AudioFile["type"] == "audio/mp3") {
                $name = md5($AudioFile['name'] . time()) . ".mp3";

                $n = $dbh->query("SELECT count_audios FROM audio_albums WHERE id='$id' LIMIT 1", PDO::FETCH_NUM);

                if($n and $n->rowCount() == 1) {
                    $n = ((int)$n->fetch()[0])+1;

                    // Если файл загружен успешно, перемещаем его
                    // из временной директории в конечную
                    $dbh->beginTransaction();
                    $dbR = $dbh->exec("insert into audios (title, src, album, `position`) VALUES ('" . $title . "', '$name', '" . $id . "', '$n')");
                    if ($dbR and $dbR == 1) {
                        $mvR = move_uploaded_file($AudioFile["tmp_name"], $_SERVER['DOCUMENT_ROOT'] . "/media/audio/" . $name);
                        if ($mvR) {
                            $dbh->exec("UPDATE audio_albums SET count_audios='$n' WHERE $id='$id' LIMIT 1");
                            $dbh->commit();
                        } else {
                            $dbh->rollBack();
                            Destroy('Не удалось переместить файл.');
                        }
                    } else {
                        Destroy('Не удалось создать запись.');
                    }
                }
            } else {
                Destroy('Неверный формат аудиозаписи.');
            }
        } else {
            Destroy('Файл не был загружен.');
        }

        echo '{"err":"null"}';
    }
}
else {
    Destroy('Не указаны данные.');
}


И, собственно, код после решения, все работает отлично:) Спасибо за наставления, Евгений Вольф.
if($id and $title and $AudioFile) {
    $id = trim($id);
    $title = trim($title);

    if($id !== '' and $title !== '') {
        if (is_uploaded_file($AudioFile["tmp_name"])) {
            if ($AudioFile["type"] == "audio/mp3") {
                $name = md5($AudioFile['name'] . time()) . ".mp3";

                $dbh->beginTransaction();
                $dbR = $dbh->exec("INSERT INTO `audios` (`title`, `src`, `album`, `position`) SELECT '$title','$name','$id', COUNT(*)+1 FROM audios WHERE album='$id' LIMIT 1");
                if ($dbR && $dbR == 1) {
                    $mvR = move_uploaded_file($AudioFile["tmp_name"], $_SERVER['DOCUMENT_ROOT'] . "/media/audio/" . $name);
                    if (!$mvR) {
                        $dbh->rollBack();
                        Destroy('Не удалось переместить файл.');
                    }
                } else {
                    Destroy('Не удалось создать запись.');
                }

                $dbh->commit();
            } else {
                Destroy('Неверный формат аудиозаписи.');
            }
        } else {
            Destroy('Файл не был загружен.');
        }

        echo '{"err":"null"}';
    }
}
else {
    Destroy('Не указаны данные.');
}
  • Вопрос задан
  • 276 просмотров
Решения вопроса 1
Wolfnsex
@Wolfnsex Куратор тега PHP
Если не хочешь быть первым - не вставай в очередь!
Может есть какая-то функция закрытия запроса и процесс будет висеть пока MySQL не закончит?
А какая разница, запаралеллится другой процесс PHP, пока тот будет висеть занимая пул, откроет новое соединение и сделает то же самое. Вы же не думаете, что PHP обрабатывает всех клиентов в 1 поток/скрипт? Пишите лог запросов, которые выполняются и будем разбираться, от чего именно такой порядок запросов, а а не какой-то другой.

А вообще, в MySQL есть транзакции и блокировки, которые как раз защищают от таких случаев, когда кто-то что-то куда-то не успел, или не записал или... Попробуйте использовать их. Главное, не забудьте, что все операции записи/обновления должны проходить в одной транзакции, тогда подобных проблем не возникнет с вероятностью близкой к абсолюту.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
gobananas
@gobananas
finishhim.ru
Скорее всего 2 скрипта одновременно запрашивают предыдущий номер, что вполне может быть, соответственно один делает +1 и другой +1 и новый номер выходит одинаковый. Как вариант надо поставить на столбец с позицией индекс типа UNIQUE если это возможно и в этом случае 2 записи с одинаковой позицией не смогут существовать.
Сложно сказать что у вас наверняка, но есть ощущение что копать надо в сторону JS либо проблем в области обмена данными PHP-JS но уж никак не MySQL
На крайняк замерьте время update что б не думалось...
Считывание номера предыдущего файла и обновление у нового (или его добавление) можно обернуть в транзакцию тогда второй запущенный экземпляр скрипта не сможет вмешаться.
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы