1. 程式人生 > >17. pt-online-schema-change

17. pt-online-schema-change

 

在平時MySQL的運維過程中,經常會遇到表結構的變更。在表比較小的時候,直接進行變更,時間較短,但是當表非常大的時候,這麼做會導致應用卡死,服務不可用。
目前InnoDB引擎是通過以下步驟來進行DDL的:
1 利用DDL之後的語句建立一張臨時表
2 在原表上加write lock,阻塞所有DML操作
3 將原表資料複製到臨時表
4 將臨時表和原表重新命名,然後drop原始表
5 釋放 write lock。

在這個DDL過程中,針對大表進行的write lock將持續非常長的時間,我們可以用為此 perconal 推出一個工具 pt-online-schema-change,在進行DDL的時候不堵塞原表的讀寫。

工作原理:
如果表有外來鍵,除非使用 --alter-foreign-keys-method 指定特定的值,否則工具不予執行。
1 建立一張和原表一樣的空表結構。
2 執行空表的DDL
3 在原表上建立觸發器,將對原表的修改操作記錄下來。
4 複製資料到新的空表中,複製完成後,應用修改記錄。
注意:如果表中已經定義了觸發器這個工具就不能工作了。
5 複製完成後在重新命名原表和新的表


============================================


create table t02(id int);


pt-online-schema-change --alter="add name varchar(50) not null default ''" \
h=192.168.100.101,P=3306,u=admin,p=admin,D=db01,t=t02 \
--dry-run --print \
--execute


pt-online-schema-change --alter="modify id int unsigned not null auto_increment primary key" \
h=192.168.100.101,P=3306,u=admin,p=admin,D=db01,t=t02 \
--dry-run --print \
--execute


pt-online-schema-change --alter="add index idx_name_score(name,score)" \
h=192.168.100.101,P=3306,u=admin,p=admin,D=db01,t=t02 \
--dry-run --print \
--execute


pt-online-schema-change --alter="drop index idx_finger" \
h=192.168.100.101,P=3306,u=admin,p=admin,D=db01,t=t02 \
--dry-run --print \
--execute

 

pt-online-schema-change --alter="engine=innodb" \
h=192.168.100.101,P=3306,u=admin,p=admin,D=db01,t=t02 \
--dry-run --print \
--execute

 


================================================

【DSN】

指定時注意大小寫敏感,“=”左右不能有空格,多個值之間用逗號分隔

1. A charset

2. D database

3. F mysql_read_default_file

4. h host

5. p password

6. P port

7. S mysql_socket

8. t table

9. u user

 

【具體執行程序解析】

現在執行一個改表語句並開啟general log 觀察一下

1
pt-online-schema-change --alter 'add column c1 int' u=username,S=/data/mysql.sock,D=test,t=a --execute
  

1. 首先就是各種show,各種set,有興趣自己去看看,主要就是對許可權的檢查,超時時間的設定,當前系統的繁忙程度;
然後就是對錶的檢查,如是否有觸發器的存在,以及如下查詢:

explain SELECT * FROM `test`.`a` WHERE 1=1;

SELECT table_schema, table_name FROM information_schema.key_column_usage
WHERE referenced_table_schema='test' AND referenced_table_name='a';

SHOW CREATE TABLE `test`.`a`;
到這裡表的情況檢查完畢


2. 現在就開始建新表,注意名字的改變,a變成了_a_new

並在這個空表上直接alter

ALTER TABLE `test`.`_a_new` add column c1 int
然後做一下檢檢視alter是否成功

SHOW CREATE TABLE `test`.`_a_new`
   

3. 建立觸發器

CREATE TRIGGER `pt_osc_test_a_del` AFTER DELETE ON `test`.`a` FOR EACH ROW DELETE IGNORE
FROM `test`.`_a_new` WHERE `test`.`_a_new`.`id` <=> OLD.`id`

CREATE TRIGGER `pt_osc_test_a_upd` AFTER UPDATE ON `test`.`a` FOR EACH ROW
REPLACE INTO `test`.`_a_new` (`id`, `name`, `type`, `b`) VALUES (NEW.`id`, <br>NEW.`name`, NEW.`type`, NEW.`b`)

CREATE TRIGGER `pt_osc_test_a_ins` AFTER INSERT ON `test`.`a` FOR EACH ROW
REPLACE INTO `test`.`_a_new` (`id`, `name`, `type`, `b`) VALUES (NEW.`id`, <br>NEW.`name`, NEW.`type`, NEW.`b`)
  

4. 通過explain來判斷執行chunk拷貝的成本,第一個chunk的大小固定為1000行,後面的chunk根據自己的指定,如chunk-time來確定大小

EXPLAIN SELECT `id`, `name`, `type`, `b` FROM `test`.`a` FORCE INDEX(`PRIMARY`)
WHERE ((`id` >= '1')) AND ((`id` <= '1000')) LOCK IN SHARE MODE

確定不會影響系統正常執行後,執行insert操作,將原始表中的資料按照當前chunk大小拷貝到新表中


INSERT LOW_PRIORITY IGNORE INTO `test`.`_a_new` (`id`, `name`, `type`, `b`)
SELECT `id`, `name`, `type`, `b` FROM `test`.`a` FORCE INDEX(`PRIMARY`)
WHERE ((`id` >= '1')) AND ((`id` <= '1000')) LOCK IN SHARE MODE
 

5. 一個chunk拷貝結束後立即對系統負載進行檢查


SHOW GLOBAL STATUS LIKE 'Threads_running'
沒問題的話就繼續explain,insert,負載太高的話就暫停拷貝等待負載降低,以此類推,直到所有拷貝結束


6. 拷貝結束後,對新表狀態是否進行檢查

ANALYZE TABLE `test`.`_a_new`
如果正常OK就往下走,如果不OK就刪掉新表或不刪報錯退出(根據引數指定),預設是刪掉


7. 確定新表沒有問題後就用新表來代替舊錶,注意舊錶的名字在這個時候也會改一下

RENAME TABLE `test`.`a` TO `test`.`_a_old`, `test`.`_a_new` TO `test`.`a`
  

8. 替換成功後預設是將舊錶刪掉

DROP TABLE IF EXISTS `test`.`_a_old`
 

9. 將之前建的觸發器刪掉

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_a_del`

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_a_upd`

DROP TRIGGER IF EXISTS `test`.`pt_osc_test_a_ins`
  

10. 最後再確定一下新表是否改名成功


SHOW TABLES FROM `test` LIKE '\_a\_new'

SHOW TABLES FROM `test` LIKE 'a'

改表完成!!!