加速MySQL的alter table操作

 ̄綄美尐妖づ 提交于 2019-12-02 10:38:54

MySQL的alter table性能在表很大的时候会出现问题。MySQL执行大部分更改操作都是新建一个需要的结构的空表,然后把所有老的数据插入到新表,最后删除旧表。这会耗费很多时间,尤其是在内存紧张,而表很大并有很多索引的时候。

  不是所有的alter table操作都会导致重建表。例如,可以通过两种方式创建或去掉列的默认值(一种快、一种慢)。下面是较慢的方式:

alter table film modify column rental_duration tinyint(3) not null default 5;
  使用show status分析该命令发现,它执行了1001次句柄读取和1000次写入。换句话说,即使列类型、大小和可空性没有变化,它也把表拷贝到了新表中。

flush status;
alter table film modify column rental_duration tinyint(3) not null default 5;
show session status like 'handle%';
Variable_name    Value
Handler_commit     2
......    
Handler_read_rnd_next    1001
......    
Handler_write    1000
  理论上,MySQL能跳过构建一个新表的方式。列的默认值实际保存在表的.frm文件中,因此可以不接触表而更改它。MySQL没有使用这种优化,而是任何modify column都会导致表重建。但是可以使用alter column改变列的默认值:

alter table film alter column rental_duration set default 5;
  这个命令更改了.frm文件并且没有改动表,它非常快。(alter table可以使用alter column、modify column、change column来修改列,每个命令做的事情都不一样)。

flush status;
alter table film modify column rental_duration tinyint(3) not null default 5;
show session status like 'handle%';
Variable_name

Value

Handler_commit     2
......    
Handler_read_rnd_next    0
......    
Handler_write    0
1. 只修改.frm文件
  下面这种技巧不被MySQL支持,也没有文档记载,而且不保证一定能工作。使用它们需要自己承担风险,建议在使用之前备份数据。

  不重建表可以执行下面的操作:

移除(不是添加)列的auto_increment属性。
添加、移除或更改enum或set常量。如果移除了一个常量,查询含有该常量的行将返回空字符串。
  基本的技巧是为想要的表结构创建一个.frm文件来替代现有的.frm文件,步骤如下:

创建一个布局完全一样的空表,但是想改动的地方除外(例如添加enum的常量)。
执行flush tables with read lock。这会关闭所有正在使用的表,并且防止任何表被打开。
交换.frm文件。
执行unlock tables释放读锁。
  例子:
  向film表的rating列添加一个常量,当前列定义如下:

show columns from film like 'rating';
Field    Type    Null    Key    Default
rating    enum('G','PG','PG-13','R','NC-17')    YES    
G
  现在增加一个PG-14:

create table film_new like film;
alter table film_new modify column rating enum('G','PG','PG-13','R','NC-17','PG-14') default 'G';
flush tables with read lock;
  现在从系统的命令行交换.frm文件:

mv film.frm film_tmp.frm
mv film_new.frm film.frm
mv film_tmp.frm film_new.frm
  回到MySQL命令行,现在可以给表解锁并查看改动是否生效;

unlock tables;
show columns from film like 'rating';
Field    Type    Null    Key    Default
rating    enum('G','PG','PG-13','R','NC-17','PG-14')    YES    
G
  最后删除用来辅助该操作的表:

drop table film_new;
  注意新值被添加到常量列表的末尾,如果放到中间,如在PG-13之后,就更改了已有数据的含义:已有R值就会变成PG-14,NC-17就会变成R,等等。

2. 快速建立MyISAM表的索引
  高效加载MyISAM表的诀窍是禁用键、加载数据、启用键:

alter table load_data disable keys;
-- load the data
alter table load_data enable keys;
  这不会有问题,因为它使MyISAM直到所有数据被加载后才建立键,在这个时候,它可以按照排序构建索引。它很快并且会得到无碎片、紧凑的索引树(MyISAM在使用load data infile和空表的时候也会按照排序创建索引)。

  不幸的是,disable keys只适用于非唯一索引。MyISAM在内存中构建唯一索引并且在加载每一行的时候检验其唯一性,一旦索引的大小超过可用内存,加载就变得极为缓慢。

  如果已经知道所有的数据都是有效的从而没有必要进行唯一性检查,可以采用下面的步骤加速这个过程(再次提醒这是不被MySQL支持,也没有文档的技巧。使用它需要承担风险,要先备份数据):

创建一个有需要的结构的表,但是没有任何索引。
把数据加载到表中,以构建.MYD文件。
创建另一个有需要结构的表,这次包含索引。这会创建.frm和.MYI文件。
用读取锁刷新该表。
重命名第2个表的.frm和.MYI文件,这样MySQL就可以把它们用在第1个表上。
释放读锁。
使用repair table创建表的索引。这会按照排序创建所有的索引,包括唯一索引。
  这个过程对很大的表也很快。
————————————————
版权声明:本文为CSDN博主「wzy0623」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wzy0623/article/details/53908035

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!