处理从添加时间戳列到mysql表的尴尬

时间:2013-12-05 22:45:42

标签: mysql alter-table calculated-columns

我有一个非常大的表,我想添加一个时间戳列。它是一个比插入更多更新的表。我试图找到一种方法来做到这一点,而不是把表从生产中移除了很长一段时间,它让我有结。我能做到:

alter table stuff add column mod_time timestamp;

好吧,我可以这样做,然后桌子被锁定3-5个小时。对用户来说不是一个快乐的时光。

例如,要添加varchar列,我可以创建一个新表,添加列,复制数据,然后用新表替换旧表。最后一次切换可以在慢速时间内在一两秒钟内完成。没问题。当然,我可能需要分阶段进行复制。我可以做一个副本,然后复制第一个副本开始后更改的所有内容。我可以重复这个,直到没有任何改变并进行交换。当然,如果我有一个“mod_time”列,更容易确定改变了什么。

我真正宁愿做的是:(1)创建新表,(2)复制数据,(3)添加时间戳列,(4)交换表。

但步骤(3)又需要几个小时。

我希望在添加此列之前更新行,以便将mod_time值设置为NULL。

如果我在上面的步骤中切换(2)和(3),交换是可行的,但我得到一个mod_time =当我做这个东西来添加列。我想要NULL。

我可以尝试在交换之前将值设置为NULL,但是当然如果更新行以将其设置为NULL,则更新行并将mod_time列设置为当前时间。 : - )

我希望我能做到:(1)创建新表,(2)添加“mod_time”作为日期时间列(2)复制数据,(3)将mod_time更改为时间戳列,(4)交换表。

理论上(3)可以非常快速地完成(假设日期时间和时间戳的存储是兼容的),因为我在将列更改为tmestamp时所做的就是改变其未来的行为,而不是当前的存储。所以这应该不花时间,是吗?可能不是。

只是为了澄清,如果我这样做:

 alter table stuff add column mod_time timestamp;

我明白了:

 +----------+--------+----------+---------------------+
 |     col1 |   col2 |     col3 | mod_time            |
 +----------+----- --+----------+---------------------+
 |     5001 |     50 |     2463 | 0000-00-00 00:00:00 |
 |     5002 |     50 |     2467 | 0000-00-00 00:00:00 |
 |     5003 |     50 |     2459 | 0000-00-00 00:00:00 |

这就是我想要的。只是alter语句需要太长时间。我尝试的其他一切都给了我:

 +----------+--------+----------+---------------------+
 |     col1 |   col2 |     col3 | mod_time            |
 +----------+----- --+----------+---------------------+
 |     5001 |     50 |     2463 | 2013-12-05 18:11:21 |
 |     5002 |     50 |     2467 | 2013-12-05 18:11:21 |
 |     5003 |     50 |     2459 | 2013-12-05 18:11:21 |

2 个答案:

答案 0 :(得分:0)

你应该使用pt-online-schema-change,一个自动执行MySQL的ALTER TABLE操作的工具,,不用锁定表。它是Percona Toolkit的一部分,是MySQL开发人员和DBA的免费帮助工具集。

$ pt-onlines-chema-change h=localhost,D=mydatabase,t=mytable \
  --alter="add column mod_time timestamp"

当pt-online-schema-change正常工作时,您可以继续读取和写入原始表。完成后,它会自动交换表格的名称,然后删除原始名称。

您可以通过其设计人员查看有关此工具的网络研讨会录像。查看是免费的,但需要注册:http://www.percona.com/webinars/2012-05-02-zero-downtime-schema-changes-in-mysql

答案 1 :(得分:0)

如下:

CREATE TABLE Stuff_new LIKE Stuff;
ALTER TABLE Stuff_new ADD COLUMN mod_time TIMESTAMP NULL; -- NULL important to get column to default to NULL, not current timestamp
DELIMITER $$
CREATE TRIGGER ON Stuff_AI AFTER INSERT ON Stuff FOR EACH ROW
BEGIN
    INSERT INTO Stuff_new (c1, c2, ..., mod_time) VALUES (NEW.c1, NEW.c2, ..., NULL);
END $$
CREATE TRIGGER ON Stuff_AU AFTER UPDATE ON Stuff FOR EACH ROW
BEGIN
    INSERT INTO Stuff_new (c1,c2,...) VALUES (NEW.c1, NEW.c2, ...)
      ON DUPLICATE KEY UPDATE c1=NEW.c1, c2=NEW.c2, ..., mod_time=NULL;
END $$
CREATE TRIGGER ON Stuff_AD AFTER DELETE ON Stuff FOR EACH ROW
BEGIN
    DELETE FROM Stuff_new WHERE c1=OLD.c1;
END $$
DELIMITER ;

基本上,您在并行表“Stuff_new”中创建新表结构,然后在Stuff上使用触发器将数据复制到Stuff_new,因为对Stuff进行了更改。经过一段时间后,Stuff_new应该足够接近Stuff,你可以在那里找到丢失数据的正确副本。

警告:现在已经很晚了,我累了......这只是一个头脑风暴!使用风险自负。 :)

(哦,这可能会有所帮助:ALTER TABLE ADD COLUMN takes a long time