Вопрос

Итак, у меня есть эта таблица аудита (отслеживает действия в любой таблице в моей базе данных):

CREATE TABLE `track_table` (
  `id` int(16) unsigned NOT NULL,
  `userID` smallint(16) unsigned NOT NULL,
  `tableName` varchar(255) NOT NULL DEFAULT '',
  `tupleID` int(16) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `action` char(12) NOT NULL DEFAULT '',
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableName`,`tupleID`,`date_insert`),
  KEY `actionDate` (`action`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1

И мне нужно начать архивирование устаревших предметов. Стол вырос примерно до 50 миллионов рядов, поэтому самый быстрый способ удаления рядов - это удалить его таблицу за раз (на основе tableName).

Это работает довольно хорошо, но в некоторых таблицах, которые тяжелыми для записи, это не будет завершено. Мой запрос удаляет все предметы, которые имеют связанные delete Действие на комбинацию Tupleid/TableName:

DELETE FROM track_table WHERE tableName='someTable' AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableName='someTable' AND action='DELETE' AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
)

Я позволил этому работать на моем сервере в течение 3 дней, и он никогда не завершался для самой большой таблицы. Объяснение вывода (если я переключаю удаление, чтобы выбрать:

| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

Так что 4 миллиона рядов не должны удерживать 3 дня, я думаю. У меня есть установлен innodb_buffer_pool_size на 3GB, и сервер не настроен на использование ONE_FILE_PER_TABLE. Какие еще способы я могу улучшить производительность InnoDB? (Запуск MySQL 5.1.43 на Mac OSX)

Это было полезно?

Решение

Вы можете удалить данные партиями.

В SQL Server синтаксис delete top X ряды из таблицы. Затем вы делаете это в цикле, с транзакцией для каждой партии (если у вас, конечно, более одного утверждения), поэтому для хранения транзакций и поддерживать замки только в течение коротких периодов.

В синтаксисе MySQL: DELETE FROM userTable LIMIT 1000

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

Есть дополнительная опасность для использования LIMIT с DELETE Когда дело доходит до репликации; Удаленные ряды иногда не удаляются в том же порядке на рабе, как это было удалено на мастере.

Другие советы

Попробуйте использовать подход Temp Table. Попробуйте что -то вроде этого:

Шаг 1) CREATE TABLE track_table_new LIKE track_table;

Шаг 2) INSERT INTO track_table_new SELECT * FROM track_table WHERE action='DELETE' AND date_insert >= DATE_SUB(CURDATE(), INTERVAL 30 day);

Шаг 3) ALTER TABLE track_table RENAME track_table_old;

Шаг 4) ALTER TABLE track_table_new RENAME track_table;

Шаг 5) DROP TABLE track_table_old;

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

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

Поскольку MySQL не поддерживает полную функцию сканирования свободного индекса, вы можете попытаться настроить последовательность для KEY actionDate (action, date_insert) к KEY actionDate (date_insert, action). Анкет С префиксом «date_insert» MySQL должен использовать этот индекс для сканирования строк, которые предшествуют вашему условию DateTime.

С таким индексом вы можете написать SQL как:

DELETE
FROM track_table
WHERE tableName='someTable'
    AND action='DELETE'
    AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
LIMIT 1000 -- Your size of batch
| id | select_type        | table       | type | possible_keys      | key     | key_len | ref        | rows    | Extra                        |
|  1 | PRIMARY            | track_table | ref  | tableID            | tableID | 257     | const      | 3941832 | Using where                  |
|  2 | DEPENDENT SUBQUERY | track_table | ref  | tableID,actionDate | tableID | 261     | const,func |       1 | Using where; Using temporary |

-Fist, из вашего объяснения key_len So Big => вам нужно понизить размер как можно меньше. Для вашего запроса я думаю, что лучший способ - это поля типа Данных Данных с Чар (12) на TinyInt, поэтому картирование данных выглядит как:

1: -> DELETE
2: -> UPDATE
3: -> INSERT
...

И вы можете изменить Table_id вместо этого. DDL для лучшей производительности банки:

CREATE TABLE `track_table` (
  `id` int(11) unsigned NOT NULL,
  `userID` smallint(6) unsigned NOT NULL,
  `tableid` smallint(6) UNSIGNED NOT NULL DEFAULT 0,
  `tupleID` int(11) unsigned NOT NULL,
  `date_insert` datetime NOT NULL,
  `actionid` tinyin(4) UNSIGNED NOT NULL DEFAULT 0,
  `className` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `userID` (`userID`),
  KEY `tableID` (`tableid`,`tupleID`,`date_insert`),
  KEY `actionDate` (`actionid`,`date_insert`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `actions` (
  `id` tinyint(4) unsigned NOT NULL 
  `actionname` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

CREATE TABLE `table_name` (
  `id` tinyint(4) unsigned NOT NULL 
  `tablename` varchar(255) NOT NULL,
  PRIMARY KEY (`id`) 
) ENGINE=InnoDB DEFAULT CHARSET=latin1;

Итак, запрос может работать как:

DELETE FROM track_table WHERE tableid=@tblid AND tupleID IN (
  SELECT DISTINCT tupleID FROM track_table
  WHERE tableid=@tblid AND actionid=@actionid AND date_insert < DATE_SUB(CURDATE(), INTERVAL 30 day)
).

Но самым быстрым способом было использование разделения. Таким образом, вы можете сбросить раздел. В настоящее время у моего стола есть около 40 миллионов строк. и обновлять почасово (обновление строк 400 тыс. Для каждого раз), и я могу отказаться от раздела Curr_Date и перезагрузить данные в таблицу. Команда Drop очень быстро (<100 мс). Надеюсь, это поможет.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с dba.stackexchange
scroll top