MySQL大字段的DDL操做:加減字段、索引、修改字段屬性等,在5.1以前都是很是耗時耗力的,特別是會對MySQL服務產生影響。在5.1以後隨着Plugin Innodb的出如今線加索引的提升了不少,可是還會影響(時間縮短了),主要是在更改期間,會生成一個互斥鎖,阻塞對整個表的全部操做。不過MySQL 5.6能夠避免上面的狀況,支持在線DDL操做了。但目前大部分在用的版本都是5.6以前的,因此DDL操做一直是運維人員頭疼的事。那如何在不鎖表的狀況下安全快速地更新表結構?如今來講明下percona-toolkit的pt-online-schema-change(簡稱:OSC)的使用說明,能夠很好的解決上述的問題。php
在咱們的之前作法中,爲了避免影響線上業務,咱們通常採用:先在線下從庫更改表結構,而後替換線上從庫,這樣一臺臺的修改,最後作一下主庫切換,這個過程會耗費很長時間,而且在作主庫切換時,風險也很是的大,咱們怎樣才能讓時間更短,且能不阻塞讀寫狀況下在線修改呢?早在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公司的腳本作詳細的剖析。html
pt-online-schema-change在線更改表結構的實現核心有以下幾個過程:(注:在跟改過程當中涉及到三個表:原表、tmp_table即做爲原表導數據的臨時表,old_table在最後rename原表的結果表)python
第一步:新建tmp_table,表結構同原表。mysql
1sql |
CREATE TABLE $db.$tmp_tbl LIKE $db.$tbl數據庫 |
第二步:在tmp_table上更改表結構爲須要的表結構。安全
1服務器 |
ALTER TABLE $db.$tmp_tbl ADD $col $type多線程 |
第三步:在原表上建立三個觸發器,以便後續原表上的操做同步到新表。app
1 2 3 |
CREATE TRIGGER `pt_osc_test_online_table_del` AFTER DELETE ON `test`.`online_table` FOR EACH ROW DELETE IGNORE FROM `test`.`_online_table_new` WHERE `test`.`_online_table_new`.`id` <=> OLD.`id` CREATE TRIGGER `pt_osc_test_online_table_upd` AFTER UPDATE ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`) CREATE TRIGGER `pt_osc_test_online_table_ins` AFTER INSERT ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`) |
建立觸發器,用於記錄從拷貝數據開始以後,對源數據表繼續進行數據修改的操做記錄下來,用於數據拷貝結束後,執行這些操做,保證數據不會丟失。若是表中已經定義了觸發器這個工具就不能工做了。咱們能夠看到這三個觸發器分別對應於INSERT、UPDATE、DELETE三種操做:
1) pt_osc_test_online_table_del,DELETE操做,咱們注意到DELETE IGNORE,當新有數據時,咱們才進行操做,也就是說,當在後續導入過程當中,若是刪除的這個數據還未導入到新表,那麼咱們能夠不在新表執行操做,由於在之後的導入過程當中,原表中改行數據已經被刪除,已經沒有數據,那麼他也就不會導入到新表中。
2) pt_osc_test_online_table_ins,INSERT操做,全部的INSERT INTO所有轉換爲REPLACE INTO,爲了確保數據的一致性,當有新數據插入到原表時,若是觸發器還未把原表數據未同步到新表,這條數據已經被導入到新表了,那麼咱們就能夠利用replace into進行覆蓋,這樣數據也是一致的。
3) pt_osc_test_online_table_upd,UPDATE操做,全部的UPDATE也轉換爲REPLACE INTO,由於當新的數據的行還未同步到新表時,新表是不存在這條記錄的,那麼咱們就只能插入該條數據,若是已經同步到新表了,那麼也能夠進行覆蓋插入,全部數據與原表也是一致的。
咱們也能看出上述的精髓也就這這幾條replace into操做,正是由於這幾條replace into才能保證數據的一致性。
第四步:拷貝原表數據到新表,數據量大時會根據主鍵進行分段chunk插入。
1 |
INSERT LOW_PRIORITY IGNORE INTO `test`.`_tt_new` (`id`, `name`) SELECT `id`, `name` FROM `test`.`tt` LOCK IN SHARE MODE /*pt-online-schema-change 21745 copy table*/ |
數據的拷貝是基於小塊(根據chunk-time參數指定)進行的,並且根據主鍵或者索引進行選擇,因此對總體服務器性能影響較小。它是經過一些查詢(基本爲主鍵、惟一鍵值)分批把數據導入到新的表中,在導入前,咱們能經過參數 --chunk-size 對每次導入行數進行控制,已減小對原表的鎖定時間,而且在導入時,咱們能經過 --sleep 參數控制,在每一個chunk導入後與下一次chunk導入開始前sleep一會,sleep時間越長,對於磁盤IO的衝擊就越小。
第五步:交換表,其經過一個RENAME TABLE同時處理兩個表,實現原子操做。
1 |
RENAME TABLE `test`.`online_table` TO `test`.`_online_table_old`, `test`.`_online_table_new` TO `test`.`online_table` |
其經過一個RENAME TABLE同時處理兩個表,實現原子操做。在rename過程,其實咱們仍是會致使寫入讀取堵塞的,因此從嚴格意思上說,咱們的OSC也不是對線上環境沒有一點影響,但因爲rename操做只是一個修更名字的過程,也只會修改一些表的信息,基本是瞬間結束,故對線上影響不太大。
第六步:清理以上過程當中的old表,觸發器。
1 2 3 4 |
DROP TABLE IF EXISTS `test`.`_tt_old` DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_del`; DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_upd`; DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_ins`; |
總結
以上即爲整個Percona OSC的過程,咱們看到精華部分就觸發器那一塊,不過還有不少細節未介紹,如:外鍵、記錄binlog等等,因爲環境的複雜性,此工具仍是有不少風險,如如下幾個方面問題或者須要規避的一些問題:
1)此工具不是原子操做,若是某一點失敗,不只僅會留下不少中間過程的垃圾文件,而這些文件很難徹底清理,而且若是有這些文件存在,那麼就不能在次執行OSC操做。
2)在執行時,儘可能避免有這個表的批量更新、鎖表、優化表的操做,咱們能想象的到,若是有鎖表、優化表那麼OSC是否還能正常執行?
3)若是存在主從結構,那麼儘可能在從庫先執行。由於若是在主庫執行完畢後再到從庫執行,咱們能想象,主庫字段多,此時數據同步到從庫,會不會有問題呢?
4)必須有單一列的主鍵或者單一惟一鍵,這樣咱們在insert select * from分片時,是否是能更好的處理量呢?
5)不要有外鍵,儘管腳本通過嚴格測試,可是是否還有bug,也未知,表的外鍵是否是會帶來更多的問題呢?
6)在執行以前,咱們是否是要對磁盤容量進行評估呢?由於OSC會使用表的一倍以上空間。
以上列到的,只是部分問題,我想若是須要在線進行實施,還須要通過嚴格的測試,可是它的實現爲咱們提供了一個很好的在線更改表結構方法,我相信只要咱們能很好的規避他的弊端,它會給咱們帶來很大的幫助,對於MySQL 5.6以前的數據庫版本,在MySQL 5.6版本開始已經支持Online DDL操做了。
具體安裝能夠參考:Percona Toolkit工具集介紹
1 |
$ pt-online-schema-change [OPTIONS] DSN |
經常使用選項說明:
--user,-u
#鏈接的用戶名
--password,-p
#鏈接的密碼
--database,-D
#鏈接的數據庫
--port,-P
#鏈接數據庫的端口
--host,-h
#鏈接的主機地址
--socket,-S
#鏈接的套接字文件
--ask-pass
#隱式輸入鏈接MySQL的密碼
--charset
#指定修改的字符集
--defaults-file,-F
#讀取配置文件
--alter
#結構變動語句,不須要alter table關鍵字,能夠指定多個更改,用逗號分隔。以下場景,須要注意:
1)不能用RENAME來重命名錶。
2)列不能經過先刪除,再添加的方式進行重命名,這會將數據拷貝到新列。
3)若是加入的列非空並且沒有默認值,則工具會失敗,其不會爲你設置一個默認值,必須顯示指定。
4)刪除外鍵(drop foreign key constrain_name)時,須要指定名稱_constraint_name,而不是原始的constraint_name。如:CONSTRAINT `fk_foo` FOREIGN KEY (`foo_id`) REFERENCES `bar` (`foo_id`),須要指定:–alter 「DROP FOREIGN KEY _fk_foo」
--alter-foreign-keys-method
#如何把外鍵引用到新表?須要特殊處理帶有外鍵約束的表,以保證它們能夠應用到新表。當重命名錶的時候,外鍵關係會帶到重命名後的表上。該工具備兩種方法,能夠自動找到子表,並修改約束關係。
1)auto:在rebuild_constraints和drop_swap兩種處理方式中選擇一個。
2)rebuild_constraints:使用ALTER TABLE語句先刪除外鍵約束,而後再添加。若是子表很大的話,會致使長時間的阻塞。
3)drop_swap: 執行FOREIGN_KEY_CHECKS=0,禁止外鍵約束,刪除原表,再重命名新表。這種方式很快,也不會產生阻塞,可是有風險:
3.1 在刪除原表和重命名新表的短期內,表是不存在的,程序會返回錯誤。
3.2 若是重命名錶出現錯誤,也不能回滾了,由於原表已經被刪除。
4)none: 相似」drop_swap」的處理方式,可是它不刪除原表,而且外鍵關係會隨着重命名轉到老表上面。
--[no]check-alter
#默認yes,語法解析。配合 --dry-run 和 --print 一塊兒運行,來檢查是否有問題(change column,drop primary key)。
--max-lag
#默認1s,每一個chunk拷貝完成後,會查看全部複製Slave的延遲狀況。要是延遲大於該值,則暫停複製數據,直到全部從的滯後小於這個值,使用Seconds_Behind_Master。若是有任何從滯後超過此選項的值,則該工具將睡眠 --check-interval 指定的時間,再檢查。若是從被中止,將會永遠等待,直到從開始同步,而且延遲小於該值。若是指定 --check-slave-lag ,該工具只檢查該服務器的延遲,而不是全部服務器。
--check-slave-lag
#指定一個從庫的DSN鏈接地址,若是從庫超過 --max-lag 參數設置的值,就會暫停操做。
--recursion-method = none
#默認是show processlist,發現從的方法,也能夠是host,但須要在從上指定report_host,經過show slave hosts來找到,能夠指定none來不檢查Slave的主從延遲。
--check-interval
#默認是1。 --max-lag 檢查的睡眠時間。
--[no]check-plan
#默認yes,檢查查詢執行計劃的安全性。
--[no]check-replication-filters
#默認yes,若是工具檢測到服務器選項中有任何複製相關的篩選,如指定binlog_ignore_db和replicate_do_db此類。發現有這樣的篩選,工具會報錯且退出。由於若是更新的表Master上存在,而Slave上不存在,會致使複製的失敗。使用 --no-check-replication-filters 選項來禁用該檢查。
--[no]swap-tables
#默認yes,交換原始表和新表,除非你禁止 --[no]drop-old-table 。
--[no]drop-triggers
#默認yes,刪除原表上的觸發器。 --no-drop-triggers 會強制開啓 --no-drop-old-table ,即不刪除觸發器就會強制不刪除原表。
--new-table-name
#複製建立新表的名稱,默認%T_new。
--[no]drop-new-table
#默認yes。刪除新表,若是複製組織表失敗。
--[no]drop-old-table
#默認yes。複製數據完成重命名以後,刪除原表。若是有錯誤則會保留原表。
--max-load
#默認爲Threads_running=25。每一個chunk拷貝完後,會檢查SHOW GLOBAL STATUS的內容,檢查指標是否超過了指定的閾值。若是超過,則先暫停。這裏能夠用逗號分隔,指定多個條件,每一個條件格式:status指標=MAX_VALUE或者status指標:MAX_VALUE。若是不指定MAX_VALUE,那麼工具會這隻其爲當前值的120%。
--critical-load
#默認爲Threads_running=50。用法基本與 --max-load 相似,若是不指定MAX_VALUE,那麼工具會這隻其爲當前值的200%。若是超過指定值,則工具直接退出,而不是暫停。
--default-engine
#默認狀況下,新的表與原始表是相同的存儲引擎,因此若是原來的表使用InnoDB的,那麼新表將使用InnoDB的。在涉及複製某些狀況下,極可能主從的存儲引擎不同。使用該選項會默認使用默認的存儲引擎。
--set-vars
#設置MySQL變量,多個用逗號分割。默認該工具設置的是:wait_timeout=10000、innodb_lock_wait_timeout=一、lock_wait_timeout=60。
--chunk-size-limit
#當須要複製的塊遠大於設置的chunk-size大小,就不復制,默認值是4.0,一個沒有主鍵或惟一索引的表,塊大小就是不肯定的。
--chunk-time
#在chunk-time執行的時間內,動態調整chunk-size的大小,以適應服務器性能的變化,該參數設置爲0。或者指定chunk-size,均可以禁止動態調整。
--chunk-size
#指定塊的大小,默認是1000行,能夠添加k,M,G後綴。這個塊的大小要儘可能與 --chunk-time 匹配,若是明確指定這個選項,那麼每一個塊就會指定行數的大小。
--[no]check-plan
#默認yes。爲了安全,查查詢的執行計劃,默認狀況下,這個工具在執行查詢以前會先EXPLAIN,以獲取一次少許的數據。若是是很差的EXPLAIN,那麼會獲取一次大量的數據,這個工具會屢次執行EXPALIN,若是EXPLAIN不一樣的結果,那麼就會認爲這個查詢是不安全的。
--statistics
#打印出內部事件的數目,能夠看到複製數據插入的數目。
--dry-run
#建立和修改新表,但不會建立觸發器、複製數據、和替換原表。並不真正執行,能夠看到生成的執行語句,瞭解其執行步驟與細節。 --dry-run 與 --execute 必須指定一個,兩者相互排斥。和–print配合最佳。
--execute
#肯定修改表,則指定該參數會真正開始執行。 --dry-run與--execute 必須指定一個,兩者相互排斥。
#打印SQL語句到標準輸出。指定此選項可讓你看到該工具所執行的語句,和 --dry-run 配合最佳。
--progress
#複製數據的時候打印進度報告,二部分組成:第一部分是百分比,第二部分是時間。
--quiet,-q
#不把信息標準輸出。
測試一:簡單使用
第一步:建立一個測試表,表引擎爲myisam
1 2 3 4 5 |
CREATE TABLE `online_table` ( `id` int(11) NOT NULL auto_increment primary key (id), `name` varchar(10) DEFAULT NULL, `age` int(11) DEFAULT NULL ) engine = myisam default charset utf8; |
PS:使用OSC時,原表須要一個主鍵或惟一索引,由於刪除的觸發器須要,不然數據不會被複制。上面已經說明了該工具的實現方式,下面來執行看是否有效。
第二步:更改存儲引擎查看OSC實現方式和原理
1 2 3 4 5 6 7 |
$ pt-online-schema-change \ --user=root \ --password=zabbixzabbix \ --host=127.0.0.1 \ --alter "engine=innodb" D=test,t=online_table \ --print \ --dry-run |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
Operation, tries, wait: analyze_table, 10, 1 copy_rows, 10, 0.25 create_triggers, 10, 1 drop_triggers, 10, 1 swap_tables, 10, 1 update_foreign_keys, 10, 1 Starting a dry run. `test`.`online_table` will not be altered. Specify --execute instead of --dry-run to alter the table. Creating new table... \\第一步,建立一個新表; CREATE TABLE `test`.`_online_table_new` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 Created new table test._online_table_new OK. Altering new table... \\第二步,更改表結構; ALTER TABLE `test`.`_online_table_new` engine=innodb Altered `test`.`_online_table_new` OK. Not creating triggers because this is a dry run. \\建立觸發器,這裏因爲使用--dry-run因此並不會建立; CREATE TRIGGER `pt_osc_test_online_table_del` AFTER DELETE ON `test`.`online_table` FOR EACH ROW DELETE IGNORE FROM `test`.`_online_table_new` WHERE `test`.`_online_table_new`.`id` <=> OLD.`id` CREATE TRIGGER `pt_osc_test_online_table_upd` AFTER UPDATE ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`) CREATE TRIGGER `pt_osc_test_online_table_ins` AFTER INSERT ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`) Not copying rows because this is a dry run. INSERT LOW_PRIORITY IGNORE INTO `test`.`_online_table_new` (`id`, `name`, `age`) SELECT `id`, `name`, `age` FROM `test`.`online_table` LOCK IN SHARE MODE /*pt-online-schema-change 21023 copy table*/ Not swapping tables because this is a dry run. Not dropping old table because this is a dry run. Not dropping triggers because this is a dry run. DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_del`; DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_upd`; DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_ins`; 2016-05-16T14:54:27 Dropping new table... DROP TABLE IF EXISTS `test`.`_online_table_new`; 2016-05-16T14:54:27 Dropped new table OK. Dry run complete. `test`.`online_table` was not altered. |
若是表沒有主鍵或惟一鍵,會出現以下這一句:
1 2 |
The new table `test`.`_online_table_new` does not have a PRIMARY KEY or a unique index which is required for the DELETE trigger. //原表須要一個主鍵或則惟一索引,由於刪除的觸發器須要,不然數據不會被複制; |
第三步:使用–execute正式修改表的存儲引擎(DDL操做)
1 2 3 4 5 6 7 |
$ pt-online-schema-change \ --user=root \ --password=zabbixzabbix \ --host=127.0.0.1 \ --alter "engine=innodb" D=test,t=online_table \ --execute \ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
No slaves found. See --recursion-method if host www.ywnds.com has slaves. Not checking slave lag because no slaves were found and --check-slave-lag was not specified. Operation, tries, wait: analyze_table, 10, 1 copy_rows, 10, 0.25 create_triggers, 10, 1 drop_triggers, 10, 1 swap_tables, 10, 1 update_foreign_keys, 10, 1 Altering `test`.`online_table`... Creating new table... //建立新表; CREATE TABLE `test`.`_online_table_new` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, `age` int(11) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=MyISAM DEFAULT CHARSET=utf8 Created new table test._online_table_new OK. Altering new table... //更改表; ALTER TABLE `test`.`_online_table_new` engine=innodb Altered `test`.`_online_table_new` OK. 2016-05-16T14:04:12 Creating triggers... //建立觸發器; CREATE TRIGGER `pt_osc_test_online_table_del` AFTER DELETE ON `test`.`online_table` FOR EACH ROW DELETE IGNORE FROM `test`.`_online_table_new` WHERE `test`.`_online_table_new`.`id` <=> OLD.`id` CREATE TRIGGER `pt_osc_test_online_table_upd` AFTER UPDATE ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`) CREATE TRIGGER `pt_osc_test_online_table_ins` AFTER INSERT ON `test`.`online_table` FOR EACH ROW REPLACE INTO `test`.`_online_table_new` (`id`, `name`, `age`) VALUES (NEW.`id`, NEW.`name`, NEW.`age`) 2016-05-16T14:04:12 Created triggers OK. 2016-05-16T14:04:12 Copying approximately 0 rows... //執行數據複製的操做,原表數據插入到新表; INSERT LOW_PRIORITY IGNORE INTO `test`.`_online_table_new` (`id`, `name`, `age`) SELECT `id`, `name`, `age` FROM `test`.`online_table` LOCK IN SHARE MODE /*pt-online-schema-change 20603 copy table*/ 2016-05-16T14:04:12 Copied rows OK. 2016-05-16T14:04:12 Swapping tables... //插入完畢後交換表; RENAME TABLE `test`.`online_table` TO `test`.`_online_table_old`, `test`.`_online_table_new` TO `test`.`online_table` 2016-05-16T14:04:12 Swapped original and new tables OK. 2016-05-16T14:04:12 Dropping old table... //刪除舊錶; DROP TABLE IF EXISTS `test`.`_online_table_old` 2016-05-16T14:04:12 Dropped old table `test`.`_online_table_old` OK. 2016-05-16T14:04:12 Dropping triggers... //刪除觸發器; DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_del`; DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_upd`; DROP TRIGGER IF EXISTS `test`.`pt_osc_test_online_table_ins`; 2016-05-16T14:04:12 Dropped triggers OK. Successfully altered `test`.`online_table`. |
上面指定了 --execute ,輸出的信息裏面也表示原表已經被修改爲功而且記錄了很詳細的操做信息,查看錶的引擎是否已經被修改:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
mysql> show table status like 'online_table'\G *************************** 1. row *************************** Name: online_table Engine: InnoDB Version: 10 Row_format: Compact Rows: 0 Avg_row_length: 0 Data_length: 16384 Max_data_length: 0 Index_length: 0 Data_free: 9437184 Auto_increment: 1 Create_time: 2016-05-16 15:46:40 Update_time: NULL Check_time: NULL Collation: utf8_general_ci Checksum: NULL Create_options: Comment: 1 row in set (0.00 sec) |
測試二:主從環境
在主從環境下,修改表結構時儘可能在從庫先執行,由於若是先在主庫執行完畢後再到從庫執行,咱們能想象,主庫字段多,數據同步到從庫,會不會有問題呢,要考慮?
另外在主從環境下,若是添加一個字段須要加上 --no-check-replication-filters 選項,語句以下。
1 2 3 4 5 6 7 8 |
$ pt-online-schema-change \ --user=root \ --password=zabbixzabbix \ --host=127.0.0.1 \ --alter "ADD COLUMN content text" D=test,t=online_table \ --execute \ --print \ --no-check-replication-filters |
若是不加 --no-check-replication-filters 選項的話,那麼就會報錯的,由於默認會檢查主從。除了add column,也能夠modify column,drop column,對於change column則須要指定: --no-check-alter 參數。
測試三:主外鍵表的基本操做
1 2 3 4 5 6 7 |
$ pt-online-schema-change \ --user=root \ --password=zabbixzabbix \ --host=127.0.0.1 \ --alter "ADD COLUMN content text" D=test,t=tt \ --execute \ |
1 2 3 4 5 6 7 8 9 10 11 12 |
No slaves found. See --recursion-method if host www.ywnds.com has slaves. Not checking slave lag because no slaves were found and --check-slave-lag was not specified. Operation, tries, wait: analyze_table, 10, 1 copy_rows, 10, 0.25 create_triggers, 10, 1 drop_triggers, 10, 1 swap_tables, 10, 1 update_foreign_keys, 10, 1 Child tables: `test`.`xx` (approx. 1 rows) You did not specify --alter-foreign-keys-method, but there are foreign keys that reference the table. Please read the tool's documentation carefully. |
執行錯誤退出,提示須要指定: --alter-foreign-keys-method 參數來操做有外鍵的表。要是沒有外鍵而加了參數的話會出現:
No foreign keys reference `aaa`.`xx`; ignoring –alter-foreign-keys-method。
1 2 3 4 5 6 7 8 |
$ pt-online-schema-change \ --user=root \ --password=zabbixzabbix \ --host=127.0.0.1 \ --alter "ADD COLUMN content text" D=test,t=tt \ --execute \ --print \ --alter-foreign-keys-method auto |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
No slaves found. See --recursion-method if host www.ywnds.com has slaves. Not checking slave lag because no slaves were found and --check-slave-lag was not specified. Operation, tries, wait: analyze_table, 10, 1 copy_rows, 10, 0.25 create_triggers, 10, 1 drop_triggers, 10, 1 swap_tables, 10, 1 update_foreign_keys, 10, 1 Child tables: //子表 `test`.`xx` (approx. 1 rows) Will automatically choose the method to update foreign keys. Altering `test`.`tt`... Creating new table... //建立新表 CREATE TABLE `test`.`_tt_new` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 Created new table test._tt_new OK. Altering new table... //修改新表 ALTER TABLE `test`.`_tt_new` ADD COLUMN content text Altered `test`.`_tt_new` OK. 2016-05-16T16:39:30 Creating triggers... //建立觸發器 CREATE TRIGGER `pt_osc_test_tt_del` AFTER DELETE ON `test`.`tt` FOR EACH ROW DELETE IGNORE FROM `test`.`_tt_new` WHERE `test`.`_tt_new`.`id` <=> OLD.`id` CREATE TRIGGER `pt_osc_test_tt_upd` AFTER UPDATE ON `test`.`tt` FOR EACH ROW REPLACE INTO `test`.`_tt_new` (`id`, `name`) VALUES (NEW.`id`, NEW.`name`) CREATE TRIGGER `pt_osc_test_tt_ins` AFTER INSERT ON `test`.`tt` FOR EACH ROW REPLACE INTO `test`.`_tt_new` (`id`, `name`) VALUES (NEW.`id`, NEW.`name`) 2016-05-16T16:39:30 Created triggers OK. 2016-05-16T16:39:30 Copying approximately 1 rows... //複製數據 INSERT LOW_PRIORITY IGNORE INTO `test`.`_tt_new` (`id`, `name`) SELECT `id`, `name` FROM `test`.`tt` LOCK IN SHARE MODE /*pt-online-schema-change 21745 copy table*/ 2016-05-16T16:39:30 Copied rows OK. 2016-05-16T16:39:30 Max rows for the rebuild_constraints method: 4000 Determining the method to update foreign keys... //處理外鍵的方式,選擇的是auto,會根據狀況進行選擇:重建或則禁用外鍵檢測。 2016-05-16T16:39:30 `test`.`xx`: 1 rows; can use rebuild_constraints 2016-05-16T16:39:30 Swapping tables... //交換表 RENAME TABLE `test`.`tt` TO `test`.`_tt_old`, `test`.`_tt_new` TO `test`.`tt` 2016-05-16T16:39:31 Swapped original and new tables OK. 2016-05-16T16:39:31 Rebuilding foreign key constraints... //重建外鍵 ALTER TABLE `test`.`xx` DROP FOREIGN KEY `xx_ibfk_1`, ADD CONSTRAINT `_xx_ibfk_1` FOREIGN KEY (`tt_id`) REFERENCES `test`.`tt` (`id`) 2016-05-16T16:39:31 Rebuilt foreign key constraints OK. 2016-05-16T16:39:31 Dropping old table... DROP TABLE IF EXISTS `test`.`_tt_old` 2016-05-16T16:39:31 Dropped old table `test`.`_tt_old` OK. 2016-05-16T16:39:31 Dropping triggers... DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_del`; DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_upd`; DROP TRIGGER IF EXISTS `test`.`pt_osc_test_tt_ins`; 2016-05-16T16:39:31 Dropped triggers OK. Successfully altered `test`.`tt`. |
對可靠性要求不高能夠用auto模式更新,要是可靠性要求高則須要用rebuild_constraints模式。即:
1 |
--alter-foreign-keys-method=rebuild_constraints |
總結
1)上面的測試都是把原表刪除了,要是不刪除原表則: --no-drop-old-table ,這樣會讓原表(_test_binlog_old)保留。
2)要是在線上環境上添加字段,但又不想影響到服務,能夠用參數: --max-load 去執行該工具,默認是Threads_running=25,即當前有這麼多線程在運行的時候就暫停數據的複製,等少於該值則繼續複製數據到新表:
3)當業務量較大時,修改操做會等待沒有數據修改後,執行最後的rename操做。所以,在修改表結構時,應該儘可能選擇在業務相對空閒時,至少修改表上的數據操做較低時,執行較爲穩當。
4)若是對外鍵表操做時,四種外鍵操做類型須要根據表的數據量和可靠程度,進行選擇。處於可靠性的緣由,儘可能使用rebuild_constraints類型,若是沒有可靠性要求,可使用auto類型。
5)因爲可能存在必定的風險,在操做以前,建議對數據表進行備份,可使得操做更安全、可靠。
使用該工具的前提是處理的表須要有主鍵或則惟一索引。當處理有外鍵的表時,須要加 --alter-foreign-keys-method 參數,值能夠根據狀況設置。當是主從環境,不在意從的延遲,則須要加 --recursion-method=none 參數。當須要儘量的對服務產生小的影響,則須要加上 --max-load 參數。