最近一個統計系統的大表須要加字段,表的引擎是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分鐘沒有最新數據。對於前臺業務固然沒法忍受,不過若是是公司的統計系統,或者內部人員使用。則徹底沒問題,影響很是小,溝通到位就行。