如何在不锁定 MySQL 的情况下更新(非常)大的表

Oll*_*lle 5 mysql myisam locking update

我有一个大表(58+ 百万条记录),它表示第二个表中两条记录(玩家和目标)之间的关系。

不幸的是,设计我们模式的人并没有考虑周全,而是选择使用用户名来表示这种关系,而不是用户记录的数字 ID。随着事情的进展(就像他们通常所做的那样),我们的用户名不再是玩家的有效唯一表示,因此我需要将这些关系转换为使用数字 ID。

多亏了 Percona Toolkit,在不加锁的情况下添加字段很容易,它提供了 pt-online-schema-change 可以在实时表上进行 ALTER。但是,填充表格可能会更棘手。

表格看起来像这样(我已经剥离了不相关字段的创建),两个未填充的字段是player_idtarget_id

CREATE TABLE `player_targets` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `player` varchar(20) NOT NULL,
  `player_id` int(10) unsigned DEFAULT NULL,
  `target` varchar(20) NOT NULL,
  `target_id` int(10) unsigned DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=58000000 DEFAULT CHARSET=latin1;

CREATE TABLE 'player_uuids' (
  `id`int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(20) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=600000 DEFAUL CHARSET=latin1;
Run Code Online (Sandbox Code Playgroud)

我打算用这样的查询填充两个新字段:

UPDATE player_targets t
INNER JOIN player_uuids u1
  ON u1.username = t.player
INNER JOIN player_uuids u2
  ON u2.username = t.target
SET
  t.player_id = u1.id,
  t.target_id = u2.id
WHERE
  t.player_id IS NULL
  OR t.player_id IS NULL;
Run Code Online (Sandbox Code Playgroud)

由于表存储关系是 MyISAM,我对文档的解释是 UPDATE 查询将锁定表,直到它完成所有行。由于桌子很大,这在现场环境中可能不会很好地工作。

最好的方法是什么?编写脚本来迭代成批的关系?将表引擎更改为 InnoDB(该表需要大量读取,我认为这是 MyISAM 的原因)?

Ric*_*mes 0

是的,批量迭代。 该博客提供了一些有关如何执行此操作的建议

它的表达方式是如何将 big s 分成块DELETE,但这些原则适用于UPDATE像你这样的 big s。

它向您展示了如何使用(通常)PRIMARY KEY使用LIMIT来决定下一个块。任何步骤都不需要扫描整个表,因为这会破坏分块的目的。