DANFA

Удаление записей большого объёма в MySQL

Если удалять записи в базе данных, большого объёма, например, через цикл, можно выхватить 500 ошибку. Как правильнее удалять записи в огромных объёмах и не получить ошибку?
Спасибо.
Первое, что приходит на ум, это "поэтапное" удаление записей - это так же, как работает пагинация: получаем общее количество записей, что необходимо удалить, разделяем, скажем, на 100 (100 - записей удаляемых за один этап) и получаем количество этапов. Каждому этапу присваиваем порядковый номер:
1 этап: 1 - 100 запись.
2 этап: 101 - 200 запись.
3 этап: 201 - 300 запись. И так далее.
К URL страницы, с которой стартует удаление, добавляем параметр stage (stage=2, stage=3 и так далее). При помощи PHP получаем текущий номер этапа ($_GET['stage']) и выполняем очередное удаление. Страницу необходимо перезагружать, с + 1 к параметру stage. Для этого можно использовать JavaScript или же HTML тег (2 - через какое время перезагрузится страница, в секундах):
<meta http-equiv="refresh" content="2">

Минус этого варианта в том, что нельзя закрывать страницу, пока процесс не будет завершен полностью. А это может затянуться на долго.

Другой вариант, более удобный и не надо ни чего ждать, страницу можно закрывать, сразу после старта удаления. Создаем таблицу, например, delete_entries, с полями:
  • table_name - имя таблицы, от куда будем удалять.
  • selection - параметры удаления для оператора WHERE.

При старте удаления большого количество записей, добавляем в данную таблицу данные для удаления. Далее пишем сценарий, что будет при каждом посещении сайта, осуществлять проверку таблицы delete_entries и при наличии в ней записи, выполнять необходимые нам операции, а это удаление, так же, по 100 записей за раз. После того, как все записи будут стерты, следует удалить данные на удаление, в таблице delete_entries.

В таблицу delete_entries можно еще добавить одно поле, что будет хранить в себе время последней выполненной операции по удалению. На случай выполнения очередного этапа удаления не раньше, чем, через 10 минут, после предыдущего этапа удаления. Это позволит снизить нагрузку на сервер.
RD 24, а разве запросом нельзя удалить сразу все нужные записи разом?:
DELETE FROM table WHERE id < 60000;

Удалит все записи, что имеют ID меньше 60000, без ошибки 500.
Не всегда, чтобы удалить хотя бы одну запись, достаточно выполнить всего один запрос. Удаление одной записи, может тянуть за собой целый ряд различных операций. Допустим, нам необходимо удалить всего одну статью из блога, после запуска удаления статьи, сценарий делает запрос на получение самой статьи, для того чтобы проверить наличие связанных со статьей записей. Это могут быть вложенные файлы в статью (Картинки, Архивы, Видео и так далее); Комментарии от пользователей; Лайки/дизлайки; Жалобы; Подписки; Количество просмотров и кем просмотрено, и еще много чего может тянуться за одной статьей. Это все надо проверить и очистить, а очищая все это надо обновлять счетчики: удаляем одну статью, значит надо отнять от общего количества статей единицу; Удаляем вложения, отнимаем количество удаляемых файлов и так же с комментами, лайками и всем остальным. Кроме того, комментарии к удаляемой статьи, тоже надо проверять на наличие лайков, вложений, жалоб, уведомлений и так далее. Поэтому пишут методы удаления, которые все эти операции совмещают в себе, а не всего один запрос на очистку. Огромное количество выполнений такого метода, может привести к ошибки. Для этого и требуется разделение удаления записей на этапы.
Slash, спасибо за столь подробный ответ.
Не могли бы вы показать, что именно надо вставлять в поле selection? Принцип удаления записи у меня, как вы описали: Тема: Удаление записей большого объёма в MySQL (Сообщение отдельно: 6540).
Чтобы сказать точно, что надо вставлять в поле selection, надо глянуть, как устроен Ваш движок. Но, так могу предположить, что вставлять надо условие для оператора WHERE. Пример:
spam = 1 AND ban = 1

И сам запрос:
'DELETE FROM ' . $row['table_name'] . '
WHERE ' . $row['selection'] . '
LIMIT 100';