pt-online-schema-change用于MySQL的在线DDL。

下面结合官方文档和general log来分析其实现原理。

测试表

mysql> show create table t2\G
*************************** 1. row ***************************
Table: t2
Create Table: CREATE TABLE `t2` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1005764 DEFAULT CHARSET=utf8
1 row in set (0.19 sec)

该表中只有1列,id,自增主键。

其中,表中已经存在一部分数据

mysql> select count(*) from t2;
+----------+
| count(*) |
+----------+
| 1005763 |
+----------+
1 row in set (0.31 sec)

利用pt-online-schema-change对该表新增一列

# pt-online-schema-change --execute --alter "ADD COLUMN c1 DATETIME" D=test,t=t2

Found 2 slaves:
test
hbase
Will check slave lag on:
test
hbase
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`.`t2`...
Creating new table...
CREATE TABLE `test`.`_t2_new` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1005764 DEFAULT CHARSET=utf8
Created new table test._t2_new OK.
Waiting forever for new table `test`.`_t2_new` to replicate to test...
Altering new table...
ALTER TABLE `test`.`_t2_new` ADD COLUMN c1 DATETIME
Altered `test`.`_t2_new` OK.
2016-11-21T12:49:18 Creating triggers...
CREATE TRIGGER `pt_osc_test_t2_del` AFTER DELETE ON `test`.`t2` FOR EACH ROW DELETE IGNORE FROM `test`.`_t2_new` WHERE `test`.`_t2_ne
w`.`id` <=> OLD.`id`CREATE TRIGGER `pt_osc_test_t2_upd` AFTER UPDATE ON `test`.`t2` FOR EACH ROW REPLACE INTO `test`.`_t2_new` (`id`) VALUES (NEW.`id`)
CREATE TRIGGER `pt_osc_test_t2_ins` AFTER INSERT ON `test`.`t2` FOR EACH ROW REPLACE INTO `test`.`_t2_new` (`id`) VALUES (NEW.`id`)
2016-11-21T12:49:18 Created triggers OK.
2016-11-21T12:49:18 Copying approximately 1005075 rows...
INSERT LOW_PRIORITY IGNORE INTO `test`.`_t2_new` (`id`) SELECT `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= ?)) AND (
(`id` <= ?)) LOCK IN SHARE MODE /*pt-online-schema-change 2352 copy nibble*/SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= ?)) ORDER BY `id` LIMIT ?, 2 /*next chun
k boundary*/Copying `test`.`t2`: 40% 00:44 remain
Copying `test`.`t2`: 82% 00:12 remain
2016-11-21T12:50:31 Copied rows OK.
2016-11-21T12:50:31 Analyzing new table...
2016-11-21T12:50:32 Swapping tables...
RENAME TABLE `test`.`t2` TO `test`.`_t2_old`, `test`.`_t2_new` TO `test`.`t2`
2016-11-21T12:50:35 Swapped original and new tables OK.
2016-11-21T12:50:35 Dropping old table...
DROP TABLE IF EXISTS `test`.`_t2_old`
2016-11-21T12:50:36 Dropped old table `test`.`_t2_old` OK.
2016-11-21T12:50:36 Dropping triggers...
DROP TRIGGER IF EXISTS `test`.`pt_osc_test_t2_del`;
DROP TRIGGER IF EXISTS `test`.`pt_osc_test_t2_upd`;
DROP TRIGGER IF EXISTS `test`.`pt_osc_test_t2_ins`;
2016-11-21T12:50:36 Dropped triggers OK.
Successfully altered `test`.`t2`.

查看general log中的输出

161017 11:22:56     1052 Connect    root@localhost on test
1052 Query set autocommit=1
1052 Query SHOW VARIABLES LIKE 'innodb\_lock_wait_timeout'
1052 Query SET SESSION innodb_lock_wait_timeout=1
1052 Query SHOW VARIABLES LIKE 'lock\_wait_timeout'
1052 Query SET SESSION lock_wait_timeout=60
1052 Query SHOW VARIABLES LIKE 'wait\_timeout'
1052 Query SET SESSION wait_timeout=10000
1052 Query SELECT @@SQL_MODE
1052 Query SET @@SQL_QUOTE_SHOW_CREATE = 1/*!40101, @@SQL_MODE='NO_AUTO_VALUE_ON_ZERO,STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'*/
1052 Query SELECT @@server_id /*!50038 , @@hostname*/
1053 Connect root@localhost on test
1053 Query set autocommit=1
1053 Query SHOW VARIABLES LIKE 'innodb\_lock_wait_timeout'
1053 Query SET SESSION innodb_lock_wait_timeout=1
1053 Query SHOW VARIABLES LIKE 'lock\_wait_timeout'
1053 Query SET SESSION lock_wait_timeout=60
1053 Query SHOW VARIABLES LIKE 'wait\_timeout'
1053 Query SET SESSION wait_timeout=10000
1053 Query SELECT @@SQL_MODE
1053 Query SET @@SQL_QUOTE_SHOW_CREATE = 1/*!40101, @@SQL_MODE='NO_AUTO_VALUE_ON_ZERO,STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'*/
1053 Query SELECT @@server_id /*!50038 , @@hostname*/

上述主要是设置会话的变量信息,包括innodb_lock_wait_timeout,wait_timeout和SQL_QUOTE_SHOW_CREATE。

         1052 Query    SHOW VARIABLES LIKE 'wsrep_on'
1052 Query SHOW VARIABLES LIKE 'version%'
1052 Query SHOW ENGINES
1052 Query SHOW VARIABLES LIKE 'innodb_version'
1052 Query SHOW VARIABLES LIKE 'innodb_stats_persistent'
1052 Query SELECT @@SERVER_ID
1052 Query SHOW GRANTS FOR CURRENT_USER()
1052 Query SHOW FULL PROCESSLIST
1052 Query SHOW SLAVE HOSTS
1052 Query SHOW GLOBAL STATUS LIKE 'Threads_running'
1052 Query SHOW GLOBAL STATUS LIKE 'Threads_running'
1052 Query SELECT CONCAT(@@hostname, @@port)
1052 Query SHOW TABLES FROM `test` LIKE 't2'
1052 Query SHOW TRIGGERS FROM `test` LIKE 't2'
1052 Query /*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, @@SQL_MODE := '', @OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, @@SQL_QUOTE_SHOW_CREATE := 1 */
1052 Query USE `test`
1052 Query SHOW CREATE TABLE `test`.`t2`
1052 Query /*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, @@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */
1052 Query EXPLAIN SELECT * FROM `test`.`t2` WHERE 1=1
1052 Query SELECT table_schema, table_name FROM information_schema.key_column_usage WHERE referenced_table_schema='test' AND referenced_table_name='t2'
1052 Query SHOW VARIABLES LIKE 'wsrep_on'
1052 Query /*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, @@SQL_MODE := '', @OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, @@SQL_QUOTE_SHOW_CREATE := 1 */

解释:

1. 查看参数变量,当前用户的权限,slave的信息,会话变量

2. 确认t2是否存在,t2上是否有触发器

3. 查看执行计划

4. 查看是否t2表是否被其它表外键关联。

           39 Query    USE `test`
39 Query SHOW CREATE TABLE `test`.`t2`
39 Query /*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, @@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */
39 Query CREATE TABLE `test`.`_t2_new` (
`id` int(11) NOT NULL AUTO_INCREMENT,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1005764 DEFAULT CHARSET=utf8
161121 12:49:18 39 Query ALTER TABLE `test`.`_t2_new` ADD COLUMN c1 DATETIME
39 Query /*!40101 SET @OLD_SQL_MODE := @@SQL_MODE, @@SQL_MODE := '', @OLD_QUOTE := @@SQL_QUOTE_SHOW_CREATE, @@SQL_QUOTE_SHOW_CREATE := 1 */
39 Query USE `test`
39 Query SHOW CREATE TABLE `test`.`_t2_new`
39 Query /*!40101 SET @@SQL_MODE := @OLD_SQL_MODE, @@SQL_QUOTE_SHOW_CREATE := @OLD_QUOTE */
39 Query CREATE TRIGGER `pt_osc_test_t2_del` AFTER DELETE ON `test`.`t2` FOR EACH ROW DELETE IGNORE FROM `test`.`_t2_new` WHERE `test`.`_t2_new`.`id` <=> OLD.`id`
39 Query CREATE TRIGGER `pt_osc_test_t2_upd` AFTER UPDATE ON `test`.`t2` FOR EACH ROW REPLACE INTO `test`.`_t2_new` (`id`) VALUES (NEW.`id`)
39 Query CREATE TRIGGER `pt_osc_test_t2_ins` AFTER INSERT ON `test`.`t2` FOR EACH ROW REPLACE INTO `test`.`_t2_new` (`id`) VALUES (NEW.`id`)

解释:

1. 根据目标表结构创建一张新表。

2. 对新表添加字段,可以看出pt-online-shema-change对表结构进行变更依赖的还是MySQL自身的Online DDL。

3. 针对目标表创建三个触发器,DELETE,UPDATE和INSERT,因为REPLACE操作只有在主键或唯一索引存在的情况下才有意义,这也就解释了为什么目标表上要有主键或唯一索引。

           39 Query    EXPLAIN SELECT * FROM `test`.`t2` WHERE 1=1
39 Query SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) ORDER BY `id` LIMIT 1 /*first lower boundary*/
39 Query SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`t2` FORCE INDEX (`PRIMARY`) WHERE `id` IS NOT NULL ORDER BY `id` LIMIT 1 /*key_len*/
39 Query EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ * FROM `test`.`t2` FORCE INDEX (`PRIMARY`) WHERE `id` >= '' /*key_len*/
39 Query EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) ORDER BY `id` LIMIT 999, 2 /*next chunk boundary*/
39 Query SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) ORDER BY `id` LIMIT 999, 2 /*next chunk boundary*/
39 Query EXPLAIN SELECT `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) AND ((`id` <= '')) LOCK IN SHARE MODE /*explain pt-online-schema-change 2352 copy nibble*/
39 Query INSERT LOW_PRIORITY IGNORE INTO `test`.`_t2_new` (`id`) SELECT `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) AND ((`id` <= '')) LOCK IN SHARE MODE /*pt-online-schema-change 2352 copy nibble*/
39 Query SHOW WARNINGS
39 Query SELECT @@SERVER_ID
39 Query SHOW GRANTS FOR CURRENT_USER()
39 Query SHOW FULL PROCESSLIST
39 Query SELECT @@SERVER_ID
39 Query SHOW GRANTS FOR CURRENT_USER()
39 Query SHOW FULL PROCESSLIST
161121 12:49:20 39 Query SELECT 'pt-online-schema-change keepalive'
161121 12:49:21 39 Query SELECT @@SERVER_ID
39 Query SHOW GRANTS FOR CURRENT_USER()
39 Query SHOW FULL PROCESSLIST
39 Query SHOW GLOBAL STATUS LIKE 'Threads_running'
39 Query EXPLAIN SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) ORDER BY `id` LIMIT 28516, 2 /*next chunk boundary*/
39 Query SELECT /*!40001 SQL_NO_CACHE */ `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) ORDER BY `id` LIMIT 28516, 2 /*next chunk boundary*/
39 Query EXPLAIN SELECT `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) AND ((`id` <= '')) LOCK IN SHARE MODE /*explain pt-online-schema-change 2352 copy nibble*/
39 Query INSERT LOW_PRIORITY IGNORE INTO `test`.`_t2_new` (`id`) SELECT `id` FROM `test`.`t2` FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '')) AND ((`id` <= '')) LOCK IN SHARE MODE /*pt-online-schema-change 2352 copy nibble*/

解释:

上述输出只包含两个chunk的选择。其它chunk的选择基本相同。

1. SHOW GLOBAL STATUS LIKE 'Threads_running'用于监控当前的系统负载。

2. 可以看出pt-online-schema-change是以chunk为单位进行目标表数据的拷贝。

3. 在拷贝的过程中,对目标表的相关记录加了共享锁,此时,会堵塞客户端对这些记录的DML操作。

           39 Query    ANALYZE TABLE `test`.`_t2_new` /* pt-online-schema-change */
161121 12:50:32 39 Query RENAME TABLE `test`.`t2` TO `test`.`_t2_old`, `test`.`_t2_new` TO `test`.`t2`
161121 12:50:35 39 Query DROP TABLE IF EXISTS `test`.`_t2_old`
161121 12:50:36 39 Query DROP TRIGGER IF EXISTS `test`.`pt_osc_test_t2_del`
39 Query DROP TRIGGER IF EXISTS `test`.`pt_osc_test_t2_upd`
39 Query DROP TRIGGER IF EXISTS `test`.`pt_osc_test_t2_ins`
39 Query SHOW TABLES FROM `test` LIKE '\_t2\_new'
161121 12:50:37 40 Quit
39 Quit

解释:

1. 在完成数据的拷贝后,会对新表执行ANALYZE操作,这样,可及时更新新表的统计信息。

官档的解释如下:

This circumvents a potentially serious issue related to InnoDB optimizer statistics. If the table being alerted is
busy and the tool completes quickly, the new table will not have optimizer statistics after being swapped. This
can cause fast, index-using queries to do full table scans until optimizer statistics are updated (usually after 10
seconds). If the table is large and the server very busy, this can cause an outage.

2. 对目标表和新表进行RENAME操作。

3. 删除原来的目标表

4. 删除触发器。

pt-online-schema-change的实现原理的更多相关文章

  1. schema change + ogg 变更手册

    Check OGG  until no data queuing in replication process:testRO:a)login  test5 –l oggmgrb)oggc)#ggsci ...

  2. Online Schema Change for MySQL

    It is great to be able to build small utilities on top of an excellent RDBMS. Thank you MySQL. This ...

  3. AppBoxFuture(四). 随需而变-Online Schema Change

      需求变更是信息化过程中的家常便饭,而在变更过程中如何尽可能小的影响在线业务是比较头疼的事情.举个车联网监控的例子:原终端设备上传车辆的经纬度数据,新的终端设备支持同时上传速度数据,而旧的车辆状态表 ...

  4. Online, Asynchronous Schema Change in F1

    F1: A Distributed SQL Database That Scales   http://disksing.com/understanding-f1-schema-change   ma ...

  5. MySQL OSC(在线更改表结构)原理

    1 OSC介绍 在我们的数据库操作中,更改表结构是一个常见的操作,而当我们的表数据量非常大时,我们更改表结构的时间是非 常的长,并且在跟改期间,会生成一个互斥锁,阻塞对整个表的所有操作,这样,对于我们 ...

  6. Online Schema Upgrade in MySQL Galera Cluster using TOI Method

    http://severalnines.com/blog/online-schema-upgrade-mysql-galera-cluster-using-toi-method     As a fo ...

  7. OSC的原理

    OSC是Online Schema Change简写,即在线架构改变.其实现步骤: 1. init,即初始化阶段,会对创建的表做一些验证工作,如检查表是否有主键,是否存在触发器或者外键等.2. cre ...

  8. Schema 与数据类型优化

    这是<高性能 MySQL(第三版)>第四章<Schema 与数据类型优化>的读书笔记. 1. 选择优化的数据类型 数据类型的选择原则: 越小越好:选择满足需求的最小类型.注意, ...

  9. MongoDB 变更流(Change Stream)介绍

    1. 什么是Change Stream Change Stream 是MongoDB用于实现变更追踪的解决方案,类似于关系数据库的触发器,但原理不完全相同: | | Change Stream | 触 ...

  10. iDB是如何运转的 一

    郑昀 创建于2015/12/2 最后更新于2015/12/4 关键词:数据库,MySQL,自动化运维,DDL,DML,SQL审核,备份,回滚,Inception,osc 提纲: 普通DBA和文艺DBA ...

随机推荐

  1. ASP.NET Core 依赖注入最佳实践——提示与技巧

    在这篇文章,我将分享一些在ASP.NET Core程序中使用依赖注入的个人经验和建议.这些原则背后的动机如下: 高效地设计服务和它们的依赖. 预防多线程问题. 预防内存泄漏. 预防潜在的BUG. 这篇 ...

  2. 对C#Chart控件使用整理

    转:https://blog.csdn.net/andrewniu/article/details/78770186 https://blog.csdn.net/andrewniu/article/d ...

  3. Json 操作

    Json简介: JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式.它基于 ECMAScript (欧洲计算机协会制定的js规范)的一个子集 ...

  4. 远程桌面web连接

      我们可以利用web浏览器搭配远程桌面技术来连接远程计算机,这个功能被称为远程桌面web连接(Remote desktop web connection),要享有此功能,请先在网络上一台window ...

  5. Redis缓存雪崩、缓存穿透、热点Key解决方案和分析

    缓存穿透 缓存系统,按照KEY去查询VALUE,当KEY对应的VALUE一定不存在的时候并对KEY并发请求量很大的时候,就会对后端造成很大的压力. (查询一个必然不存在的数据.比如文章表,查询一个不存 ...

  6. Spring hibernate 事务的流程

    1 在业务方法开始之前 ①获取session ②把session和当前线程绑定,这样就可以在Dao中使用SessionFactory的getCurrentSession()方法来获取session了 ...

  7. PHP 与 YAML

    PHP 与 YAML 这一段时间都没有写blog,并不是因为事情多,而是自己变懒了.看到新技术也不愿意深入思考其背后的原理,学习C++语言了近一个多月,由于学习方法有问题,并没有什么项目可以练手.靠每 ...

  8. 【项目 · Wonderland】UML设计

    团队作业---UML设计 Part 0 · 简要目录 Part 1 · 团队分工 Part 2 · UML Part 3 · 工具选择 Part 1 · 团队分工 Part 2 · UML 描述信息: ...

  9. 阿里八八Alpha阶段Scrum(6/12)

    今日进度 叶文滔: 修复了无法正确判断拖曳与点击的BUG,并且成功连接添加界面. 会议内容 会议照片 明日安排 叶文滔: 继续完善按钮功能 王国超: 继续攻克日程界面显示存在的BUG 俞鋆: 继续进行 ...

  10. HTTP协议详解之url与会话管理

    1 当我们访问一个网址的时候,这中间发生了什么 输入网址——浏览器查找域名的IP地址——浏览器给Web服务器发送一个HTTP请求——服务端处理请—— 服务端发回一个HTTP响应——浏览器渲染显示HTM ...