前幾天因為需求調整,需要在某張表中添加一個新的字段,而要添加字段的表,正好是我們庫中最大的一張表,表中大約有3300萬條記錄,占用空間32.5G,直接在表上添加字段,很容易卡死。
網上找了一下大數據量表添加字段的帖子,帖子數量還是很多的,套路也都差不多,主要就是兩種方案:
方案一:通過創建備份表的方式,步驟如下:
1.創建臨時表account_bill_temp
create table account_bill_temp like account_bill;
2.在新表中添加字段
alter table account_bill_temp add columu bill_id varchar(64) comment '賬單id' after bill_amount;
3.把舊表中的數據遷移到新表中
insert into account_bill_temp (column1,column2,...) select column1,column2,... from account_bill;
4.修改兩張表的表名
rename table account_bill to account_bill_bak;
rename table account_bill_temp to account_bill;
方案二:通過復制數據文件的方式,步驟如下:
1.將表中的數據放到數據文件中
select * from account_bill into outfile '/mysql-files/account.txt';
2.創建臨時表account_bill_temp
create table account_bill_temp like account_bill;
3.把舊表中的數據遷移到新表中
load data infile '/mysql-files/account.txt' into table account_bill_temp;
大家可能發現以上兩種方案是有區別的,方法二中並沒有添加字段的操作。其實最開始,我是按照方案一添加的字段,但是在執行第三步insert into ... select ...語句的時候,數據庫卡死了。然后我就又去網上扒拉,發現了方案二。但是方案二有個問題,就是要求臨時表的表結構和原表一致,無法添加字段,只能用於備份數據等操作,所以方案二對我們的問題其實是沒啥意義的。之所以把方案二也列出來,是因為如果只是備份數據的話,方案二的執行效率要比方案一高很多(據說能達到20倍以上)。
方案二走不通,於是我們只能又回到了方案一。方案一的主要問題就是執行第三步insert into ... select ...語句的時候會卡死,數據遷移到3100萬左右的時候,臨時表中的數據不再發生變化,數據容量也卡在了25.9G不再增加。卡死的原因沒有定位到,我們只是試着去尋找效率更高的方案。其實最終的方案也比較簡單,就是account_bill表存在5個索引,其中一個索引由6個字段組成,我們在方案一的第一步后面加了一步,刪除主鍵和所有索引。
第一次按方案一執行的時候,帶着主鍵和索引執行insert into ... select ...語句,從開始到遷移3100萬數據卡死,執行了3個小時左右。第二次執行,刪除主鍵和索引,執行insert into ... select ...語句,全部數據遷移只用了半小時。數據遷移后再把主鍵和索引加上,添加主鍵用了20分鍾,添加5個索引的耗時都在10分鍾以下,一個多小時完成整張表的遷移工作。總結最終方案如下:
1.創建臨時表account_bill_temp
create table account_bill_temp like account_bill;
2.刪除臨時表的主鍵和索引
alter table account_bill_temp drop PRIMARY KEY;
alter table account_bill_temp drop index index_name; //注意修改索引名
3.在新表中添加字段
alter table account_bill_temp add columu bill_id varchar(64) comment '賬單id' after bill_amount;
4.把舊表中的數據遷移到新表中
insert into account_bill_temp (XXX,XXX,...) select XXX,XXX,... from account_bill;
5.添加主鍵和索引
alter table account_bill_temp add primary key (id);
alter table account_bill_temp add index index_name (column1,column2);
6.修改兩張表的表名
rename table account_bill to account_bill_bak; rename table account_bill_temp to account_bill;