最近一个统计系统的大表须要加字段,表的引擎是myisam,表大小在3亿,物理文件在106G。想一想都蛋疼。那么这种状况下怎么把字段撸上去呢?mysql
1. 首先想到了《高性能MySQL》提到的直接更改表结构文件(frm),可是在通过测试之后,发现提示表损坏了,须要repair,只好放弃了。sql
2. 使用pt-online-schema-change,刚开始跑没有问题,后面在凌晨发现影响业务了,也只好放弃了。bash
3. 最近GitHub开源的gh-ost,属于新鲜玩意,尚未研究,只好放弃。服务器
4. 建立新表,load数据,最后rename表。(前提是表只有insert,表是myisam引擎)性能
最后使用了第四种方案把字段加上了。那么下面就来详细说说第三种方案。测试
咱们假设要把tb_yayun表加两个字段,uid,age。ui
老表(业务在使用的表):spa
mysql> show create table tb_yayun\G *************************** 1. row *************************** Table: tb_yayun Create Table: CREATE TABLE `tb_yayun` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` char(20) DEFAULT NULL, `enter_time` datetime NOT NULL, PRIMARY KEY (`id`), KEY `enter_time` (`enter_time`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 1 row in set (0.00 sec)
环境准备:code
1. 一台空闲的服务器,没跑业务,安装了mysql实例的。在该服务器上面建立新表。blog
mysql> show create table tb_yayun_new\G *************************** 1. row *************************** Table: tb_yayun_new Create Table: CREATE TABLE `tb_yayun_new` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` char(20) DEFAULT NULL, `enter_time` datetime NOT NULL, `uid` int(11) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`), KEY `enter_time` (`enter_time`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 1 row in set (0.00 sec)
2. 在线上服务器导出tb_yayun表的数据(这里有一个技巧,不须要所有导出,截止到某一天就行。)能够用下面下面命令:
mysql -uroot -p -q -s -e "use test;select *,'','' from tb_yayun where enter_time >= '2016-08-01 00:00:00'" > /data/tb_yayun.txt
3. 把导出的文件拷贝到上面提到的空闲服务器导入(时间会很长,我当时导入3亿的表花了6小时):
LOAD DATA INFILE '/data/tb_yayun.txt' INTO TABLE tb_yayun_new;
4. 和开发肯定一个切换时间;咱们的数据都是先入队列,因此是能够暂停一下子写入的。和开发肯定好一个时间之后,好比要在2016-08-02 15:00:00之后切换,那么此时还须要作下面工做。还须要补一次数据,由于新表的数据只导入到了2016-08-01 00:00:00。因此再次从线上服务器导数据。
mysql -uroot -p -q -s -e "use test;select *,'','' from tb_yayun where enter_time >= '2016-08-02 00:00:00' and enter_time <= '2016-08-02 15:00:00' > /data/02_tb_yayun.txt
再次拷贝到空闲的服务器导入:
LOAD DATA INFILE '/data/02_tb_yayun.txt' INTO TABLE tb_yayun_new;
5. 当导入完成之后,把tb_yayun_new表的物理文件拷贝到线上服务器。(MYD,MYI,frm),注意权限。若是线上有1主3从,那么4台服务器都须要拷贝。拷贝完成之后执行flush tables,而后每台服务器检查表是否正常。limit一下或者count一下都行。
6. 通知开发中止写入,通常是把程序中止一下子。具体时间不会超过10分钟。当开发说已经停了导入数据的程序之后,咱们要看看老表是否还有数据写入,对于myisam表来讲直接count看条数是否有变化就行。若是没有数据写入之后。执行下面的命令:
(1)再次从老服务器导数据,咱们须要把数据补一致。(线上服务器)
mysql -uroot -p -q -s -e "use test;select *,'','' from tb_yayun where enter_time >= '2016-08-02 15:00:00' > /data/15_tb_yayun.txt
(2)load数据到tb_yayun_new(注意:会致使从库延时,具体延时多久看导入的数据大小)
LOAD DATA INFILE '/data/15_tb_yayun.txt' INTO TABLE tb_yayun_new;
(3)对比新表老表数据是否一致。若是操做没有错误的话,数据确定是一致的。新表(tb_yayun_new),老表(tb_yayun)进行count确认。
(4)老表进行rename操做
alter table tb_yayun rename to tb_yayun_old_20160802;
(5)新表rename操做
alter table tb_yayun_new rename to tb_yayun;
7. 通知开发那边开启数据导入程序。至此大表加字段完成。
总结:
上面提到的方法有很是大的局限性,好比必须是myisam表,该表只有insert,还有就是业务可以忍受5-10分钟没有最新数据。对于前台业务固然没法忍受,不过若是是公司的统计系统,或者内部人员使用。则彻底没问题,影响很是小,沟通到位就行。