DDL类型
copy/inplace/instant 复制、原地、即时
copy复制算法
原理
Copy 算法在执行 DDL 操作时,会创建一个新的临时表,该临时表具有修改后的表结构。然后将原表中的数据逐行复制到新的临时表中。复制完成后,删除原表,并将临时表重命名为原表的名称。
优点
兼容性好:几乎可以处理所有类型的 DDL 变更,不受存储引擎内部机制的限制。因为它是通过创建新表来实现结构变更,所以对各种复杂的变更场景都能很好地支持。
数据一致性高:在复制数据的过程中,可以保证数据的完整性和一致性。即使在复制过程中出现错误,也可以很方便地回滚操作,不会影响原表的数据。
缺点
性能开销大:需要额外的磁盘空间来存储临时表,并且数据复制过程会消耗大量的 I/O 和 CPU 资源。如果表的数据量非常大,复制操作可能会花费很长时间,导致数据库在操作期间性能严重下降。
锁表时间长:在复制过程中,通常需要对原表加锁,以防止数据的不一致。这会导致其他用户无法对原表进行读写操作,影响数据库的可用性。
适用场景
当需要进行复杂的表结构变更,而存储引擎不支持其他更高效的方式时,可以使用 Copy 算法。例如,在某些情况下需要修改表的字符集、添加或删除大字段等。
如要进行一个新增列操作,那么进行的流程为:
-
复制一个相同表结构的新表
#sql-...
,对新表进行DDL操作,将原表中的数据复制过去 -
删除原表
-
将
#sql-...
名字修改回T
use mysql;
drop table if exists users;
create table users(id int, age int);
insert into users(id,age) values(1,1),(2,2),(3,3);
修改DDL语句:ALTER TABLE users MODIFY COLUMN id INT PRIMARY KEY AUTO_INCREMENT;
课堂练习
space id
会不会变?
在 InnoDB 存储引擎里,数据和索引存储在表空间中,每个表空间都有一个与之对应的 SPACE ID
。表空间可以是共享的(多个表共享一个表空间),也可以是独立的(每个表有自己单独的表空间),SPACE ID
能帮助 MySQL 准确地定位和管理这些表空间。
会变
修改前SPACE
为22:
mysql> SELECT * FROM INFORMATION_SCHEMA.innodb_tables WHERE NAME = 'mysql/users';
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
| TABLE_ID | NAME | FLAG | N_COLS | SPACE | ROW_FORMAT | ZIP_PAGE_SIZE | SPACE_TYPE | INSTANT_COLS | TOTAL_ROW_VERSIONS |
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
| 1095 | mysql/users | 33 | 5 | 22 | Dynamic | 0 | Single | 0 | 0 |
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
1 row in set (0.00 sec)
修改后SPACE
为23:
mysql> SELECT * FROM INFORMATION_SCHEMA.innodb_tables WHERE NAME = 'mysql/users';
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
| TABLE_ID | NAME | FLAG | N_COLS | SPACE | ROW_FORMAT | ZIP_PAGE_SIZE | SPACE_TYPE | INSTANT_COLS | TOTAL_ROW_VERSIONS |
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
| 1089 | mysql/users | 33 | 6 | 23 | Dynamic | 0 | Single | 0 | 0 |
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
1 row in set (0.00 sec)
rows_examined
是多少?
可以看到执行sql后的提示显示,影响了3行。
mysql> ALTER TABLE users MODIFY COLUMN id INT PRIMARY KEY AUTO_INCREMENT;
Query OK, 3 rows affected (0.07 sec)
Records: 3 Duplicates: 0 Warnings: 0
- 中间删除表后,
select
语句是否会报表不存在的错?
不会,应该是元数据锁?
inplace原地算法
静态添加索引c,加索引过程中不进行业务的DML。
该过程需要对
课堂练习
space id
会不会变?
不会
rows_examined
是多少?
N,需要读取数据
全程
a.ibd
需要加锁,只能读不能写
动态添加索引c,加索引过程中同步进行业务的DML。
在
alter_log
应用到构建后的a.ibd
需要加锁,只能读不能写。其他时间不加
与之相关存在系统参数:innodb_online_alter_log_max_size
。若达到最大值,会发生回滚。
新索引紧凑吗?
不紧凑,因为应用
alter_log
顺序是不固定的。为了避免该场景,mysql会为每个page页面预留空间。
instant即时算法
相关资料:https://www.cnblogs.com/huaweiyun/p/18464467
直接修改元数据,如添加一列:alter table users add x int;
如果是小表的话,可能会看不到效果。
修改前:
mysql> SELECT * FROM INFORMATION_SCHEMA.innodb_tables WHERE NAME = 'mysql/users';
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
| TABLE_ID | NAME | FLAG | N_COLS | SPACE | ROW_FORMAT | ZIP_PAGE_SIZE | SPACE_TYPE | INSTANT_COLS | TOTAL_ROW_VERSIONS |
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
| 1099 | mysql/users | 33 | 5 | 33 | Dynamic | 0 | Single | 0 | 0 |
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
1 row in set (0.01 sec)
修改后:
mysql> alter table users add x int;
Query OK, 0 rows affected (0.05 sec)
Records: 0 Duplicates: 0 Warnings: 0mysql> SELECT * FROM INFORMATION_SCHEMA.innodb_tables WHERE NAME = 'mysql/users';
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
| TABLE_ID | NAME | FLAG | N_COLS | SPACE | ROW_FORMAT | ZIP_PAGE_SIZE | SPACE_TYPE | INSTANT_COLS | TOTAL_ROW_VERSIONS |
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
| 1100 | mysql/users | 33 | 6 | 34 | Dynamic | 0 | Single | 0 | 0 |
+----------+-------------+------+--------+-------+------------+---------------+------------+--------------+--------------------+
1 row in set (0.00 sec)
所以应该使用大表做测试,所以可以参考《MySQL训练营-准备阶段》的内容,导入tpch
的数据进行测试。可以对其中的lineitem
表进行加列测试:alter table lineitem add x int;
即使这样还是走不了instant
算法,即使制定算法也不行,不知道是不是mysql的bug:
mysql> alter table lineitem add x int,ALGORITHM=INSTANT;
ERROR 1845 (0A000): ALGORITHM=INSTANT is not supported for this operation. Try ALGORITHM=COPY/INPLACE.
instant DDL语句有没有可能执行时间很长(比如超过1分)?
session1 | session2 | session3 |
---|---|---|
begin; select * from t limit 1; |
||
alter table t add g int; //现象? | ||
select * from t limit 1; //现象? |
1、session2执行alter table t add g int;
语句会被堵住,因为session1会获取MDL
锁
2、select * from t limit 1;
也会被堵住,它需要获取表 t
的 MDL
读锁。然而,由于第二个会话正在等待获取 MDL
写锁,MySQL
为了保证操作的顺序和一致性,不会让第三个会话获取 MDL
读锁,因此第三个会话也会被阻塞,直到第二个会话获取到 MDL
写锁并完成操作,或者第一个会话释放 MDL
读锁。