【问题标题】:Deal with awkwardness from adding timestamp column to mysql table处理将时间戳列添加到 mysql 表的尴尬
【发布时间】:2013-12-05 22:45:42
【问题描述】:

我有一个非常大的表,我想在其中添加一个时间戳列。这是一个比插入获得更多更新的表。我试图找出一种方法来做到这一点,而不会使桌子在很长一段时间内停止生产,这让我陷入了困境。我能做到:

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 |

【问题讨论】:

  • 你的stuff表有什么样的使用模式?它有很多 INSERT、UPDATE 和 DELETE 吗?时间戳是 INSERT 时间戳吗?或者你会在时间戳上做很多更新吗?
  • 创作相对较少,更新较多。

标签: mysql alter-table calculated-columns


【解决方案1】:

您应该使用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

【讨论】:

  • 嗯,除了行 mod_time 值都设置为 mod 时间而不是 NULL 之外,它可以工作。
  • 我想我可以做一个 --alter="add column mod_time datetime" 然后 --alter="change column mod_time mod_time timestamp"。由于没有人阻止,这是可以接受的。
  • 不。 mod_time 列中仍有非 NULL 值。好吧,对于我们不修改或添加时间戳列的 alter table 操作,这个工具很不错。
  • 您应该阅读有关 TIMESTAMP 列的默认行为的信息。可以覆盖此行为,以便在添加列时它们保持 NULL。 dev.mysql.com/doc/refman/5.6/en/timestamp-initialization.html
  • 哇。这不是很清楚。似乎“alter table stuff add column mod_time TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP;”会工作,但它不会。需要执行“更改表内容添加列 mod_time TIMESTAMP NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP;”得到我想要的行为。好吧,至少那行得通!
【解决方案2】:

怎么样:

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 上的触发器将数据复制到 Stuff_new。随着时间的流逝,Stuff_new 应该足够接近 Stuff,您可以在其中正确复制丢失的数据。

告诫购买者:时间不早了,我累了……这只是一场头脑风暴!使用风险自负。 :)

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

【讨论】:

  • 只是一次头脑风暴,您基本上已经在这里重新发明了 mysql 复制。 :-) 而且,是的,有点复杂。我发现感冒药有助于我安眠。
  • 他基本上是重新发明了 pt-online-schema-change。 :-)
猜你喜欢
  • 2012-09-05
  • 1970-01-01
  • 1970-01-01
  • 2018-03-10
  • 2019-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多