漫漫长路,其修远兮!
分类: Mysql/postgreSQL
2013-11-05 14:15:52
随着表的增大,表结构的变更变得越来越耗时,因为表结构变更时会禁止更新操作,而这会影响到了在线业务的处理。
虽然在双主复制机制下,可以通过更新备库->切换主备->同步主库的方式进行升级,但是使用支持在线DDL的工具才更方便可靠。
本文探讨和测试Percona工具集中pt-online-schema-change的原理与用法。
前提:由于pt-online-schema-change会更新数据,为避免意外,使用前最好先备份数据哦~
pt-online-schema-change的作用:在不锁表的情况下安全快速地更新表结构。
pt-online-schema-change [OPTIONS] DSN
pt-online-schema-change模拟了MySQL内部alter table的方式,但是其操作所更新的是复制表,所以原表不会被锁住。其原理我们通过示例进行解读:
shell> pt-online-schema-change –nocheck-replication-filters –recursion-method=none –alter “add newcol int” h=192.168.10.118,P=3306,u=sup,p=supch18_2db,D=dbteamdb,t=user –alter-foreign-keys-method rebuild_constraints –print –statistics –execute
Operation, tries, wait:
copy_rows, 10, 0.25
create_triggers, 10, 1
drop_triggers, 10, 1
swap_tables, 10, 1
update_foreign_keys, 10, 1
Child tables:
`dbteamdb`.`user_privs` (approx. 1 rows)
Will use the rebuild_constraints method to update foreign keys.
Altering `dbteamdb`.`user`…
Creating new table…
#步骤1,创建空表,其命名规则是_+原表名+_new
CREATE TABLE `dbteamdb`.`_user_new` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`host` varchar(20) COLLATE utf8_bin NOT NULL,
`eng_name` varchar(20) COLLATE utf8_bin NOT NULL,
`chn_name` varchar(20) COLLATE utf8_bin NOT NULL,
`create_time` datetime NOT NULL,
`update_time` datetime NOT NULL,
`status` tinyint(3) unsigned NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user__host` (`host`)
) ENGINE=InnoDB AUTO_INCREMENT=48 DEFAULT CHARSET=utf8 COLLATE=utf8_bin
Created new table dbteamdb._user_new OK.
Altering new table…
#步骤2,根据语句更新新表结构
ALTER TABLE `dbteamdb`.`_user_new` add newcol int
Altered `dbteamdb`.`_user_new` OK.
Creating triggers…
#步骤3,在原表上创建触发器,以便后续原表上的操作同步到新表
CREATE TRIGGER `pt_osc_dbteamdb_user_del` AFTER DELETE ON `dbteamdb`.`user` FOR EACH ROW DELETE IGNORE FROM `dbteamdb`.`_user_new` WHERE `dbteamdb`.`_user_new`.`id` <=> OLD.`id`
CREATE TRIGGER `pt_osc_dbteamdb_user_upd` AFTER UPDATE ON `dbteamdb`.`user` FOR EACH ROW REPLACE INTO `dbteamdb`.`_user_new` (`id`, `host`, `eng_name`, `chn_name`, `create_time`, `update_time`, `status`) VALUES (NEW.`id`, NEW.`host`, NEW.`eng_name`, NEW.`chn_name`, NEW.`create_time`, NEW.`update_time`, NEW.`status`)
CREATE TRIGGER `pt_osc_dbteamdb_user_ins` AFTER INSERT ON `dbteamdb`.`user` FOR EACH ROW REPLACE INTO `dbteamdb`.`_user_new` (`id`, `host`, `eng_name`, `chn_name`, `create_time`, `update_time`, `status`) VALUES (NEW.`id`, NEW.`host`, NEW.`eng_name`, NEW.`chn_name`, NEW.`create_time`, NEW.`update_time`, NEW.`status`)
Created triggers OK.
Copying approximately 44 rows…
#步骤4,拷贝原表数据到新表,数据量大时会根据主键进行分段chunk插入
INSERT LOW_PRIORITY IGNORE INTO `dbteamdb`.`_user_new` (`id`, `host`, `eng_name`, `chn_name`, `create_time`, `update_time`, `status`) SELECT `id`, `host`, `eng_name`, `chn_name`, `create_time`, `update_time`, `status` FROM `dbteamdb`.`user` LOCK IN SHARE MODE /*pt-online-schema-change 9378 copy table*/
Copied rows OK.
Swapping tables…
#步骤5,拷贝完成后,移走原表,用新表代替(RENAME TABLE)。其通过一个RENAME TABLE同时处理两个表,实现原子操作。
RENAME TABLE `dbteamdb`.`user` TO `dbteamdb`.`_user_old`, `dbteamdb`.`_user_new` TO `dbteamdb`.`user`
Swapped original and new tables OK.
Rebuilding foreign key constraints…
#如果更新的表是被子表外键引用的父表,那么需要相应的更新子表的外键指向。具体见下文”外键的影响”。
ALTER TABLE `dbteamdb`.`user_privs` DROP FOREIGN KEY `fk_user_privs__user_id`, ADD CONSTRAINT `_fk_user_privs__user_id` FOREIGN KEY (`user_id`) REFERENCES `dbteamdb`.`user` (`id`)
Rebuilt foreign key constraints OK.
#最后清除临时生成的表、触发器。默认情况下会删除原表(此时是已经重命名过的_user_old)。
Dropping old table…
DROP TABLE IF EXISTS `dbteamdb`.`_user_old`
Dropped old table `dbteamdb`.`_user_old` OK.
Dropping triggers…
DROP TRIGGER IF EXISTS `dbteamdb`.`pt_osc_dbteamdb_user_del`;
DROP TRIGGER IF EXISTS `dbteamdb`.`pt_osc_dbteamdb_user_upd`;
DROP TRIGGER IF EXISTS `dbteamdb`.`pt_osc_dbteamdb_user_ins`;
Dropped triggers OK.
# Event Count
# ================== =====
# INSERT 1
# rebuilt_constraint 1
Successfully altered `dbteamdb`.`user`.
注意:如果原表上已经有触发器,那么将无法使用本工具!
数据的拷贝是基于小块(根据chunk-time参数指定)进行的,而且根据主键或者索引进行选择,所以对整体服务器性能影响较小。
相关选项:
而且,如果检测到服务器压力大,则会暂停或者退出,详见–max-load和–critical-load选项。
有诸多措施来保障安全使用该工具:
外键使得以上操作变得更加复杂,如果有外键引用到要变更的表,那么就无法自动RENAME,因为外键还是引用到老表。工具必须更新外键引用到新表。其提供两种方式来实现这个功能(–alter-foreign-keys-method)。
相关选项:
外键还有一个问题,就是新建表的外键名称和旧表的外键名称重复,工具保证最终表与原表有同样的外键和索引,但是名称可能会稍有不同,会添加前缀下划线。
本工具会活动信息输出到标准输出STDOUT。在数据拷贝阶段,会打印–progress报告到STDERR。指定–print可以得到额外的信息。如果指定–statistics,会进一步打印出内部事件的数目。
1)添加一列,并不真正执行
pt-online-schema-change –alter “add column c1 int” D=mydb,t=mytable –dry-run
2)更新存储引擎为InnoDB,不删除原表
pt-online-schema-change –alter “ENGINE=InnoDB” –no-drop-old-table –print –statistics –execute D=mydb,t=mytable –execute
3)复制环境下,忽略日志筛选和Slave复制延迟,删除表字段
pt-online-schema-change –no-check-replication-filters –recursion-method=none –alter “drop company_type,drop channel_code” h=192.168.10.14,P=3370,u=user1,p=pass1,D=db1,t=table1 –print –statistics –execute
4)更新被子表引用到的父表
pt-online-schema-change –alter “add newcol int” h=192.168.10.14,P=3370,u=user1,p=pass1,D=db1,t=table1 –alter-foreign-keys-method auto –print –statistics –execute
5)在我们的双主复制环境中,设定了忽略mysql库的复制,不是很在乎复制的延迟,有时有外键影响,希望尽量保留原表数据,必要时自行删除。
pt-online-schema-change –no-check-replication-filters –recursion-method=none –alter “drop newcol” h=192.168.10.14,P=3370,u=user1,p=pass1,D=db1,t=table1 –alter-foreign-keys-method auto –no-drop-old-table –print –statistics –execute
-------------------------------------------------------
选项
–nocheck-replication-filters:如果工具检测到服务器选项中有任何复制相关的筛选,如指定binlog_ignore_db和replicate_do_db此类。发现有这样的筛选,工具会报错且退出。其原因是,如果更新的表Master上存在,而Slave上不存在,会导致复制的失败。使用–no-check-replication-filters选项来禁用该检查
–recursion-method:
–alter-foreign-keys-method : “
–statistics:会进一步打印出内部事件的数目
–max-laq:
–max-load:
–critical-load:
–lock-wait-timeout:<整型> 默认为1,即会话innodb_lock_wait_timeout的值为1秒。这个选项只针对InnoDB引擎。
–alter: