无论发生什么事,在操作结束时,我们都会寻找 ghost 表。它还在,然后我们知道操作失败了。它不在,然后它被重命名为 tbl,并且该操作以原子方式工作。
关于失败的一个注意事项是清理 _tbl_del,也许只是让它存在下去,避免重新创建它;或者如果你喜欢,你可以删除它。
对应用程序的影响?
应用程序连接保证被阻止,直到交换 ghost 表或直到操作失败。在前者中,他们继续在新表上进行操作。在后者中,他们继续在原表上进行操作。
对复制的影响?
复制从库只能看到 RENAME,二进制日志中没有 LOCK。因此,复制会看到一个原子两表交换,没有表中断。
为什么需要锁定源表?
我们知道,RENAME 操作是 in-place 的,无需重建表并允许并发的 DML 操作。但因为 GH-OST 是通过异步方式应用二进制日志内容,有可能二进制日志有一些积压。因此,我们锁定源表,以确保我们完整应用完二进制日志相关的改动。
RENAME TABLES under LOCK TABLES
值得注意的是,从 MySQL 8.0.13 版本开始,支持了在 LOCK TABLES 之上的 RENAME TABLE 操作,专门为 GH-OST 做了一些设计支持。see: https://mysqlserverteam.com/the-mysql-8-0-13-maintenance-release-is-generally-available/
简单翻译过来就是,允许在 LOCK TABLES 之上执行 RENAME TABLE (WL#9826)操作,由 Dmitry Lenev 实现了原子 RENAME TABLE。这项特性已经请求 Shlomi Noach 在 GH-OST 工具的最后 cut-over 阶段使用。此类工具使用现有的旧表与用户并行构建一个新的,经过更改的表。当新的表数据是最新的时候,需要做两个表的原子交换。现在可使用 LOCK TABLES tbl WRITE 来实现(停止更新旧表),然后同一个会话执行 RENAME TABLE tbl TO _tbl_del, _tbl_gho TO tbl(交换时 _tbl_gho 是最新的)。
# MySQL 8.0.18
mysql> LOCK TABLES t1 WRITE;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from t1;
+---+
| a |
+---+
| 2 |
+---+
1 row in set (0.01 sec)
mysql> update t1 set a=1;
Query OK, 1 row affected (0.01 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> RENAME TABLE t1 to t11;
Query OK, 0 rows affected (0.02 sec)
mysql> select * from t1;
ERROR 1100 (HY000): Table 't1' was not locked with LOCK TABLES
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from t1;
ERROR 1146 (42S02): Table 'sbtest.t1' doesn't exist
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# MySQL 8.0.18
mysql>LOCKTABLESt1WRITE;
QueryOK,0rowsaffected(0.01sec)
mysql>select*fromt1;
+---+
|a|
+---+
|2|
+---+
1rowinset(0.01sec)
mysql>updatet1seta=1;
QueryOK,1rowaffected(0.01sec)
Rowsmatched:1Changed:1Warnings:0
mysql>RENAMETABLEt1tot11;
QueryOK,0rowsaffected(0.02sec)
mysql>select*fromt1;
ERROR1100(HY000):Table't1'wasnotlockedwithLOCKTABLES
mysql>unlocktables;
QueryOK,0rowsaffected(0.00sec)
mysql>select*fromt1;
ERROR1146(42S02):Table'sbtest.t1'doesn'texist