如何以原子方式将行从一个表移动到另一个表?

时间:2011-08-03 11:28:42

标签: mysql sql atomic

我从几千个传感器收集读数并将它们存储在MySQL数据库中。每秒有几百个插入。为了提高插入性能,我将这些值最初存储到MEMORY缓冲区表中。每分钟我运行一个存储过程,将插入的行从内存缓冲区移动到永久表。

基本上我想在我的存储过程中执行以下操作来从临时缓冲区中移动行:

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

不幸的是前一个不可用,因为数据收集过程在上面的INSERT和DELETE之间的“data_buffer”中插入了额外的行。因此,这些行将被删除而不会插入“数据”表。

如何使操作成为原子或使DELETE语句只删除前面语句中SELECTed和INSERTed的行?

如果可能的话,我希望以标准的方式在不同的数据库引擎上运行。

由于性能开销和存储要求,我不希望添加任何额外的“id”列。

我希望标准SQL中有SELECT_AND_DELETE或MOVE语句或类似的东西......

6 个答案:

答案 0 :(得分:3)

我相信这会有效,但会在插入完成之前阻止

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRANSACTION;
INSERT INTO data (SELECT * FROM data_buffer FOR UPDATE); 
DELETE FROM data_buffer; 
COMMIT TRANSACTION;

答案 1 :(得分:1)

如何获得行ID,在插入前获取最大值,进行插入然后删除记录< = max(id)

答案 2 :(得分:1)

避免所有这些问题并保持快速的一种可能方法是使用两个data_buffer表(让我们称之为data_buffer1data_buffer2);当集合流程插入data_buffer2时,您可以在insert上执行deletedata_buffer2;比你切换,所以收集的数据进入data_buffer2,而数据被插入+从data_buffer1删除到data

答案 3 :(得分:1)

这是@ 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特有的。

答案 4 :(得分:0)

我假设这些表是相同的,具有相同的列和主键?如果是这种情况,你可以在where子句中嵌入select ...就像这样:

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

答案 5 :(得分:0)

这是MySQL特定的解决方案。您可以使用锁定来防止INSERTING进程在移动行时添加新行。

移动行的过程应如下:

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

INSERT缓冲区中新行的代码应更改如下:

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

当锁定到位时,INSERT进程显然会被阻塞。