OSC實現原理剖析

 、背景

在我們的數據庫操作中,更改表結構是一個常見的操作,而當我們的表數據量非常大時,我們更改表結構的時間是非常的長,並且在跟改期間,會生成一個互斥鎖,阻塞對整個表的所有操作,這樣,對於我們線上數據來說是無法容忍的,而我們怎麼能在線修改表結構而不影響線上業務呢?這就本文所要介紹的Online Schema Change(簡稱:OSC)

在我們的以前做法中,爲了不影響線上業務,我們一般採用:先在線下從庫更改表結構,然後替換線上從庫,這樣一臺臺的修改,最後做一下主庫切換,這個過程會耗費很長時間,並且在做主庫切換時,風險也非常的大,我們怎樣才能讓時間更短,且能不阻塞讀寫情況下在線修改呢?早在2008年Shlomi Noach 就利用觸發器的原理,開發了python版本oak-online-alter-table在線更改表結構腳本,最近,Percona 公司在自己的percona-toolkit腳本集合中也發佈了在線更改表結構的perl版本腳本pt-online-schema-change,Facebook公司也開發自己的在線更改表結構php版本腳本 OnlineSchemaChange.php,而它們最底層的實現原理都爲觸發器。因爲oak-online-alter-table不確定是否在被開發與支持,而OnlineSchemaChange.php的使用比較複雜,且有很多功能不支持如:表結構中如果有外鍵的情況,故在此我就Percona公司的腳本做詳細的剖析;

二、詳細實現原理及過程

pt-online-schema-change在線更改表結構的實現核心有如下幾個過程:

(注:在跟改過程中涉及到三個表:原表、tmp_table即作爲原表導數據的臨時表,old_table在最後rename 原表的結果表)

1、       CREATE TABLE `$db`.`$tmp_tbl` LIKE `$db`.`$tbl`" 新建tmp_table,表結構同原表

2、       在tmp_table上更改表結構爲需要的表結構

3、       在原表上建立三個觸發器,如下:

(1)CREATE TRIGGER mk_osc_del AFTER DELETE ON $table " "FOR EACH ROW "

"DELETE IGNORE FROM $new_table ""WHERE $new_table.$chunk_column = OLD.$chunk_column";

(2)CREATE TRIGGER mk_osc_ins AFTER INSERT ON $table " "FOR EACH ROW "

 "REPLACE INTO $new_table ($columns) "  "VALUES($new_values)";

(3)CREATE TRIGGER mk_osc_upd AFTER UPDATE ON $table " "FOR EACH ROW "

"REPLACE INTO $new_table ($columns) " "VALUES ($new_values)";

     我們可以看到這三個觸發器分別對應於INSERT、UPDATE、DELETE三種操作,

(1)     mk_osc_del,DELETE操作,我們注意到DELETE IGNORE,當新有數據時,我們才進行操作,也就是說,當在後續導入過程中,如果刪除的這個數據還未導入到新表,那麼我們可以不在新表執行操作,因爲在以後的導入過程中,原表中改行數據已經被刪除,已經沒有數據,那麼他也就不會導入到新表中;

(2)     mk_osc_ins,INSERT操作,所有的INSERT INTO全部轉換爲REPLACE INTO,爲了確保數據的一致性,當有新數據插入到原表時,如果觸發器還未把原表數據未同步到新表,這條數據已經被導入到新表了,那麼我們就可以利用replace into進行覆蓋,這樣數據也是一致的

(3)     mk_osc_upd UPDATE操作,所有的UPDATE也轉換爲REPLACE INTO,因爲當跟新的數據的行還未同步到新表時,新表是不存在這條記錄的,那麼我們就只能插入該條數據,如果已經同步到新表了,那麼也可以進行覆蓋插入,所有數據與原表也是一致的;

我們也能看出上述的精髓也就這這幾條replace into操作,正是因爲這幾條replace into才能保證數據的一致性

4、       拷貝原表數據到臨時表中,在腳本中使用如下語句

INSERT IGNORE INTO $to_table ($columns) "  "SELECT $columns FROM $from_table " "WHERE ($chunks->[$chunkno])",我們能看到他是通過一些查詢(基本爲主鍵、唯一鍵值)分批把數據導入到新的表中,在導入前,我們能通過參數--chunk-size對每次導入行數進行控制,已減少對原表的鎖定時間,並且在導入時,我們能通過—sleep參數控制,在每個chunk導入後與下一次chunk導入開始前sleep一會,sleep時間越長,對於磁盤IO的衝擊就越小

5、       Rename 原表到old表中,在把臨時表Rename爲原表,

"RENAME TABLE `$db`.`$tmp_tbl` TO `$db`.`$tbl`"; 在rename過程,其實我們還是會導致寫入讀取堵塞的,所以從嚴格意思上說,我們的OSC也不是對線上環境沒有一點影響,但由於rename操作只是一個修改名字的過程,也只會修改一些表的信息,基本是瞬間結束,故對線上影響不太大

6、       清理以上過程中的不再使用的數據,如OLD表

  以上即爲整個Percona OSC的過程,我們看到精華部分就觸發器那一塊,不過還有很多細節我未介紹,如:外鍵、記錄binlog(默認情況是不記錄binlog的)等等,由於環境的複雜性,此工具還是有很多風險,如以下幾個方面問題或者需要規避的一些問題:

1、    此工具不是原子操作,如果某一點失敗,不僅僅會留下很多中間過程的垃圾文件,而這些文件很難完全清理,並且如果有這些文件存在,那麼就不能在次執行OSC操作;

2、    在執行時,儘量避免有這個表的批量更新、鎖表、優化表的操作,我們能想象的到,如果有鎖表、優化表那麼OSC是否還能正常執行?

3、    如果存在主從結構,那麼儘量在從庫先執行,因爲如果在主庫執行完畢後在到從庫執行,我們能想象,主庫字段多同步到從庫,會不會有問題呢?

4、    必須是單一列的主鍵或者單一唯一鍵,這樣我們在insert select *from分片時,是不是能更好的處理量呢?

5、    不要有外鍵,儘管腳本經過嚴格測試,但是是否還有bug,也未知,表的外鍵是不是會帶來更多的問題呢?

6、    在執行之前,我們是不是要對磁盤容量進行評估呢?因爲OSC會使用表的一倍以上空間。

以上列到的,只是部分問題,我想如果需要在線進行實施,還需要經過嚴格的測試,但是它的實現爲我們提供了一個很好的在線更改表結構方法,我相信只要我們能很好的規避他的弊端,它會給我們帶來很大的幫助;

使用方法:

pt-online-schema-change h=*,u=* p=**,P=* ,D=enk,t=my1 --alter "add is_sign_1 int(11) unsigned NOT NULL DEFAULT '0'" --drop-old-table [--sleep 10]   

               .

參考文檔:

1、 http://www.percona.com/doc/percona-toolkit/pt-online-schema-change.html

2、 http://openarkkit.googlecode.com/svn/trunk/openarkkit/doc/html/oak-online-alter-table.html

3、 http://www.facebook.com/notes/mysql-at-facebook/online-schema-change-for-mysql/430801045932

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章