Как атомарно переместить строки из одной таблицы в другую? - PullRequest
5 голосов
/ 03 августа 2011

Я собираю показания с нескольких тысяч датчиков и сохраняю их в базе данных MySQL. Есть несколько сотен вставок в секунду. Чтобы улучшить производительность вставки, я сохраняю значения изначально в буферной таблице MEMORY. Раз в минуту я запускаю хранимую процедуру, которая перемещает вставленные строки из буфера памяти в постоянную таблицу.

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

INSERT INTO data SELECT * FROM data_buffer;
DELETE FROM data_buffer;

К сожалению, предыдущий не может быть использован, потому что процессы сбора данных вставляют дополнительные строки в «data_buffer» между INSERT и DELETE выше. Таким образом, эти строки будут удалены без вставки в таблицу «data».

Как сделать атомарную операцию или сделать оператор DELETE, чтобы удалить только те строки, которые были ВЫБРАНЫ и ВСТАВЛЕНЫ в предыдущем операторе?

Я бы предпочел сделать это стандартным способом, который работает на разных движках базы данных, если это возможно.

Я бы предпочел не добавлять дополнительные столбцы "id" из-за снижения производительности и требований к хранилищу.

Хотелось бы, чтобы в стандартном SQL или чем-то подобном был оператор SELECT_AND_DELETE или MOVE ...

Ответы [ 6 ]

3 голосов
/ 03 августа 2011

Я полагаю, что это будет работать, но будет блокироваться, пока вставка не будет завершена

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
INSERT INTO data (SELECT * FROM data_buffer FOR UPDATE); 
DELETE FROM data_buffer; 
COMMIT TRANSACTION;
1 голос
/ 04 августа 2011

Это решение похоже на ответ @ ammoQ.Разница в том, что вместо того, чтобы процесс INSERTing определил, в какую таблицу записывать данные, вы можете прозрачно поменять таблицы в запланированной процедуре.

Используйте RENAME в запланированной процедуре для замены таблиц:

CREATE TABLE IF NOT EXISTS data_buffer_new LIKE data_buffer;
RENAME TABLE data_buffer TO data_buffer_old, data_buffer_new TO data_buffer;
INSERT INTO data SELECT * FROM data_buffer_old;
DROP TABLE data_buffer_old;

Это работает, потому что оператор RENAME меняет таблицы атомарно, поэтому процессы INSERTing не завершатся с ошибкой «таблица не найдена».Это специфично для MySQL.

1 голос
/ 03 августа 2011

Одним из возможных способов избежать всех этих проблем, а также оставаться быстрым, было бы использовать две data_buffer таблицы (назовем их data_buffer1 и data_buffer2); в то время как процессы сбора данных вставляются в data_buffer2, вы можете выполнить insert и delete для data_buffer2; чем вы переключаетесь, поэтому собранные данные попадают в data_buffer2, а данные вставляются + удаляются из data_buffer1 в data.

1 голос
/ 03 августа 2011

Как насчет наличия идентификатора строки, получить максимальное значение перед вставкой, выполнить вставку и затем удалить записи <= max (id) </p>

0 голосов
/ 04 августа 2011

Это решение для MySQL. Вы можете использовать блокировку, чтобы запретить процессам INSERTing добавлять новые строки во время их перемещения.

Процедура перемещения строк должна быть следующей:

LOCK TABLE data_buffer READ;
INSERT INTO data SELECT * FROM data_buffer;
DELETE FROM data_buffer;
UNLOCK TABLE;

Код, который вставляет новые строки в буфере, должен быть изменен следующим образом:

LOCK TABLE data_buffer WRITE;
INSERT INTO data_buffer VALUES (1, 2, 3);
UNLOCK TABLE;

Процесс INSERT, очевидно, будет блокироваться, пока блокировка установлена.

0 голосов
/ 03 августа 2011

Я предполагаю, что таблицы идентичны, с одинаковыми столбцами и первичными ключами?Если это так, вы можете расположить select внутри предложения where ... что-то вроде этого:

DELETE FROM data_buffer 
WHERE primarykey IN (SELECT primarykey FROM data)
...