从前有些开发人员问我,如果在MySQL的一个大表上加字段怎么办?我一般都让他们尽可能停应用去做。直到MySQL升级到8以后这个问题得到了大大的缓解。
MySQL8的特性借鉴了Oracle 11G的设计,将表结构放在数据字典上管理。使得可以快速增加字段。但是有些场景上我还是遇到过盲点。因为有时候字段扩长度,我觉得很慢但是相关DBA告诉我很快。有时候我觉得应该很快,但是相关DBA告诉我很慢。
后来我研究了一下,发现这个和字段的长度有关。
这里可以复现一下,构造了一个1000万的表。
mysql> select count(*) from thousand;
+----------+
| count(*) |
+----------+
| 10000000 |
+----------+
1 row in set (2.55 sec)
下面是表结构。
mysql> desc thousand;
+-------+-------------+------+-----+---------+-------+
| Field | Type | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| id | int | YES | | NULL | |
| a | int | YES | | NULL | |
| name | varchar(10) | YES | | NULL | |
| time | datetime(6) | YES | | NULL | |
+-------+-------------+------+-----+---------+-------+
4 rows in set (0.08 sec)
给这个表增加一个字段。
mysql> alter table thousand add address varchar(100);
Query OK, 0 rows affected (0.15 sec)
Records: 0 Duplicates: 0 Warnings: 0
可以看到0.1秒完成。这就是效果
mysql> alter table thousand add QQ varchar(10) default '0';
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
新加字典带有默认值,可以看到30毫秒完成。这也是效果。
在Oracle11G的时候快速增加字段要求非空且要有默认值。如果仅有一个是不可以的。在MySQL8中可以看到没有非空,也没有默认值也是可以快速加字段的。
当然如果增加了,更加没有问题,可以快速增加字段。
mysql> alter table thousand add status varchar(20) default '0' not null;
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
那么来到今天的重点。把name字段从10扩大到70。看到花了将近4分钟。可见没有快速DDL的效果,这个等于重建了表。所以1000万数据花了4分钟。
mysql> alter table thousand modify name varchar(70);
Query OK, 10000000 rows affected (3 min 53.94 sec)
Records: 10000000 Duplicates: 0 Warnings: 0
把name字段从70扩大到270。
mysql> alter table thousand modify name varchar(270);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
依然可以看到是20毫秒完成。
这里涉及到的问题就是,数据字典存储64位长度以下的和存储64位以上是不一样的。
从下面的实验复现一下。只要超过64位就是重建。换句话说小于64位的长度在这个区间内从小到大扩大是可以快速DDL的。大于64位的,从小小到大扩大也是可以快速DDL的。
mysql> alter table thousand add tel varchar(20);
Query OK, 0 rows affected (0.26 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table thousand modify tel varchar(60);
Query OK, 0 rows affected (0.03 sec)
Records: 0 Duplicates: 0 Warnings: 0
mysql> alter table thousand modify tel varchar(65);
Query OK, 10000000 rows affected (3 min 45.94 sec)
Records: 10000000 Duplicates: 0 Warnings: 0
mysql> alter table thousand modify tel varchar(165);
Query OK, 0 rows affected (0.02 sec)
Records: 0 Duplicates: 0 Warnings: 0
但是就是主要如果从小于64扩大到超过64位,则需要考虑会发生表重构的可能。
当然缩小也是一样的会发生表重构的情况。