Каков наилучший способ удаления старых строк из MySQL на скользящей основе?

Я нахожусь в том, что хочу удалять строки старше (x) -дней в скользящей основе во многих приложениях. Каков наилучший способ сделать это наиболее эффективно в таблице с высоким трафиком?

Например, если у меня есть таблица, в которой хранятся уведомления, и я хочу сохранить их только 7 дней. Или высокие оценки, которые я хочу сохранить только 31 день.

Прямо сейчас я сохраняю строку, хранящую опубликованное время эпохи, и запускаю задание cron, которое выполняется один раз в час и удаляет их с такими значениями:

DELETE FROM my_table WHERE time_stored < 1234567890 LIMIT 100

Я делаю это до тех пор, пока mysql_affected_rows не вернет 0.

Я делал все это сразу, но это заставило все в приложении зависнуть в течение 30 секунд или около того, пока складывается INSERTS. Добавление LIMIT сработало, чтобы облегчить это, но мне интересно, есть ли лучший способ сделать это.

Ответ 1

Отъезд Разделение MySQL:

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

См. этот пост, чтобы получить некоторые идеи о том, как его применять:

Использование планировщика разделов и планировщика событий для обрезки архивных таблиц

И этот:

Разделение по датам: быстрое руководство

Ответ 2

Попробуйте создать Событие, которое будет автоматически запускаться в базе данных по желанию.

Вот пример: Если вы хотите удалить записи из более чем 30 дней из таблицы "tableName", имея запись столбца "datetime". Затем каждый запрос запускается каждый день, который будет выполнять необходимые действия по очистке.

CREATE EVENT AutoDeleteOldNotifications
ON SCHEDULE AT CURRENT_TIMESTAMP + INTERVAL 1 DAY 
ON COMPLETION PRESERVE
DO 
DELETE LOW_PRIORITY FROM databaseName.tableName WHERE datetime < DATE_SUB(NOW(), INTERVAL 30 DAY)

Нам нужно добавить ON COMPLETION PRESERVE, чтобы сохранить событие после каждого прогона. Вы можете найти более подробную информацию здесь: http://www.mysqltutorial.org/mysql-triggers/working-mysql-scheduled-event/

Ответ 3

Вместо того, чтобы выполнять удаление только в таблице, попробуйте сначала собрать соответствующие ключи, а затем выполнить DELETE JOIN

Учитывая пример запроса выше

DELETE FROM my_table WHERE time_stored < 1234567890 LIMIT 100 ;

Вы можете оставить LIMIT из него.

Предположим, вы хотите удалить данные, которые старше 31 дня.

Вычислить 31 день в секундах (86400 X 31 = 2678400)

  • Начните с сбора ключей
  • Затем индексируйте клавиши
  • Затем выполните DELETE JOIN
  • Наконец, отбросьте собранные ключи

Вот алгоритм

CREATE TABLE delete_keys SELECT id FROM my_table WHERE 1=2;
INSERT INTO delete_keys
SELECT id FROM
(
    SELECT id FROM my_table
    WHERE time_stored < (UNIX_TIMESTAMP() - 2678400)
    ORDER BY time_stored
) A LIMIT 100;
ALTER TABLE delete_keys ADD PRIMARY KEY (id);
DELETE B.* FROM delete_keys
INNER JOIN my_table B USING (id);
DROP TABLE delete_keys;

Если сбор ключей составляет менее 5 минут, запустите этот запрос каждые 5 минут.

Дайте ему попробовать!!!

ОБНОВЛЕНИЕ 2012-02-27 16:55 EDT

Вот что-то, что должно ускорить сбор ключей немного больше. Добавьте следующий индекс:

ALTER TABLE my_table ADD INDEX time_stored_id_ndx (time_stored,id);

Это улучшит поддержку подзапроса, который заполняет таблицу delete_keys, потому что это обеспечивает индекс покрытия, чтобы поля извлекались только из индекса.

ОБНОВЛЕНИЕ 2012-02-27 16:59 EDT

Поскольку вам нужно часто удалять, вы можете попробовать это каждые два месяца.

OPTIMIZE TABLE my_table;

Это приведет к дефрагментации таблицы после всех этих раздражающих маленьких удалений каждые 5 минут в течение двух месяцев.

Ответ 4

В моей компании мы имеем аналогичную ситуацию. У нас есть таблица, содержащая ключи с истечением срока действия. У нас есть cron, который работает, чтобы очистить это:

DELETE FROM t1 WHERE expiration < UNIXTIME(NOW());

Это выполнялось раз в час, но у нас были похожие проблемы с тем, что вы испытываете. Мы увеличили его до одного раза в минуту. Затем 6 раз в минуту. Настройте cron с bash script, который в основном выполняет запрос, затем спит в течение нескольких секунд и повторяется до тех пор, пока не будет достигнута минута.

Увеличенная частота значительно уменьшала количество строк, которые мы удаляли. Это облегчило спор. Это маршрут, по которому я пойду.

Однако, если вы обнаружите, что у вас по-прежнему слишком много строк для удаления, используйте ограничение и выполняйте спящий режим между ними. Например, если у вас есть 50k строк для удаления, сделайте 10k кусок с 2-секундным сном между ними. Это поможет запросам укладываться, и это позволит серверу выполнять некоторые нормальные операции между этими массовыми удалениями.

Ответ 5

Возможно, вам захочется рассмотреть возможность внедрения решения master/slave (replication) в ваш дизайн. Если вы переместите весь прочитанный трафик на ведомое устройство, вы откроете мастер, чтобы обрабатывать действия "на лету" CRUD, которые затем реплицируются на подчиненный (ваш сервер чтения).

И поскольку вы удаляете столько записей, вы можете захотеть запустить optimize в таблице (таблицах), из которой строки удаляются.

Ответ 6

Закончилось использование этого, чтобы оставить только 100 последних строк на месте, поэтому значительное отставание при выполнении часто (каждую минуту)

delete a from tbl a left join (
    select ID
    from tbl
    order by id desc limit 100
) b on a.ID = b.ID
where b.ID is null;