当前位置:网站首页>不要再使用MySQL online DDL了
不要再使用MySQL online DDL了
2022-08-01 18:58:00 【InfoQ】
一、pt-online-schema-change
1.1 主要工作流程
CREATE TRIGGER `pt_osc_test_t1_ins`
AFTER INSERT ON `test`.`t1` FOR
EACH ROW REPLACE INTO `test`.`_t1_new` (`id`, `col1`, `col2`, `col3`)
VALUES (NEW.`id`, NEW.`col1`, NEW.`col2`, NEW.`col3`);CREATE TRIGGER `pt_osc_test_t1_del`
AFTER DELETE ON `test`.`t1` FOR
EACH ROW DELETE IGNORE FROM `test`.`_t1_new`
WHERE `test`.`_t1_new`.`id` <=> OLD.`id`CREATE TRIGGER `pt_osc_test_t1_upd`
AFTER UPDATE ON `test`.`oldmapping` FOR EACH ROW
BEGIN
DELETE IGNORE FROM `test`.`_t1_new`
WHERE !(OLD.`id` <=> NEW.`id`) AND `test`.`_t1_new`.`id` <=> OLD.`id`;
REPLACE INTO `test`.`_t1_new` (`id`, `col1`, `col2`, `col3`)
VALUES (NEW.`id`, NEW.`col1`, NEW.`col2`, NEW.`col3`);
ENDSELECT /*!40001 SQL_NO_CACHE */ `id` FROM t1 FORCE INDEX(`PRIMARY`) ORDER BY `id` LIMIT 1 /*first lower boundary*/
// 假设返回数据是 1SELECT /*!40001 SQL_NO_CACHE */ `id` FROM t1 FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '1')) ORDER BY `id` LIMIT 999, 2 /*next chunk boundary*/
// `id` >= '1' 中 1 是 a步骤中获取的,也就是本次循环的下边界值
// LIMIT 999,2 中 999 和 --chunk-size 设置有关,--chunk-size 减 1;2是固定的。假如返回两条数据1000,10001,那么第一条数据1000就是本次循环的上边界,第二条数据10001是下次循环的下边界INSERT LOW_PRIORITY IGNORE INTO `_t1_new` (`id`, `c1`, `c2`, `c3`) SELECT `id`, `c1`, `c2`, `c3`
FROM t1 FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '1')) AND ((`id` <= '1000')) LOCK IN SHARE MODE /*pt-online-schema-change 11260 copy nibble*/
// 1000 是步骤b中获取的上边界值
SELECT /*!40001 SQL_NO_CACHE */ `id` FROM t1 FORCE INDEX(`PRIMARY`) WHERE ((`id` >= '10001')) ORDER BY `id` LIMIT 999, 2 /*next chunk boundary*/
// 10001 是上次循环执行步骤b获取的下边界值- 若上面只返回一条数据,则本次循环刚好能将未同步的的数据全部同步,数据的上边界就是此次获取的 id 值;此次数据同步完,进入步骤 5 ;
- 若上面返回数据为空,则本次循环也能将未同步的的数据全部同步,且同步的数据量小于 --chunk-size 设置的数量,需要执行下面语句获取此次循环的上边界。
- 更改表名:RENAME TABLE t1 TO _t1_old, _t1_new TO t1;
- 删除原始表:删除原始表 _t1_old 和触发器。
1.2 使用限制和风险
1.2.1使用限制
- 原始表上必须有主键或者唯一键,因为创建的 DELETE 触发器依赖主键或者唯一键进行数据同步;不过,若原始表上没有主键或者唯一键,但是即将执行的变更包含创建主键或唯一键的操作也可以;
- 原始表上不能存在触发器;
- pt-online-schema-change 适用于 Percona XtraDB Cluster (PXC) 5.5.28-23.7 及更高版本,但有两个限制:只能更改 InnoDB 表,并且 wsrep_OSU_method 必须设置为 TOI。如果主机是集群节点并且表是 MyISAM 或正在转换为 MyISAM (ENGINE=MyISAM),或者wsrep_OSU_method 不是 TOI,则该工具将退出并报错。
1.2.2 使用风险
The tool should handle this correctly, but you should test it first because if it fails the renamed columns' data will be lost! Specify --no-check-alter to disable this check and perform the --alterThe tool should handle this correctly, but you should test it first because if it fails the renamed columns' data will be lost! Specify --no-check-alter to disable this check and perform the --alterpt-online-schema-change -u user -ppasswd -h127.0.0.1 -P3308 D=test,t=ptosc --alter "change id id_new int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'primary key' " --print --dry-run --check-alterUsing original table index PRIMARY for the DELETE trigger instead of new table index PRIMARY because the new table index uses column id_new which does not exist in the original table.
CREATE TRIGGER `pt_osc_test_ptosc_del` AFTER DELETE ON `test`.`ptosc` FOR EACH ROW DELETE IGNORE FROM `test`.`_ptosc_new` WHERE `test`.`_ptosc_new`.`id` <=> OLD.`id`- 更改有外键引用的表的结构或者属性
- rebuild_constraints:该方式会在第六步更改表名后,执行一个原子操作先删除外键再重新添加外键。
- drop_swap:该方式会在第六步更改表名前删除原始表,然后将影子表重命名。这会导致表短暂的不存在,若是对表的查询很频繁会导致错误。
- none:该方式执行的操作和处理无外键引用的表是相同的,但是外键实际上是引用了已经删除的表。
- 创建唯一索引或者主键
- 锁争用问题
1.3 丰富的监控功能
- 该工具可以监控变更期间主从延迟情况,默认是监控所有的从库,若发现有其中一个从库延迟时间超过了 --max-lag 参数设置的数值,则该工具会停止新旧表表之间的数据同步,直到复制延迟低于 --max-lag 设置的数值 。由于生产环境很多时候是一主多从的架构,我们可能只关心某一(几)台从库的延迟情况,这个时候可以使用 --check-slave-lag参数指定需要关注的从库节点。
- 该工具可以监控变更期间数据库的负载情况,其对应选项为 --max-load 或者 --critical-load。
- 该工具默认设置 innodb_lock_wait_timeout=1 and (for MySQL 5.5 and newer) lock_wait_timeout=60 ,以防在发生锁争用时阻塞其他正常业务的事务执行。若要更改或者设置其他参数,可以通过 --set-vars 参数设置。
二、gh-ost
2.1 主要工作流程
select /* gh-ost `test`.`t1` */ `id` from `test`.`t1` order by `id` asc limit 1
// 确定全部需要同步数据范围的下边界MIN(id)
select /* gh-ost `test`.`t1` */ `id` from `test`.`t1` order by `id` desc limit 1
// 确定全部需要同步数据范围的上边界MAX(id)select /* gh-ost `test`.`t1` */ `id`
from `test`.`t1`
where ((`id` > _binary'8991')) and ((`id` < _binary'10000') or ((`id` = _binary'10000')))
order by `id` asc
limit 1
offset 998
// 其中 8991是上次循环的上边界值
// 10000 是需要同步数据范围的上边界MAX(id)
// offset 998 这个值和--chunk-size的设置有关,--chunk-size值减 1insert /* gh-ost `test`.`t1` */ ignore into `test`.`_t1_gho` (`id`, `c1`, `c2`, `c3`)
(select `id`, `c1`, `c2`, `c3` from `test`.`t1`
force index (`PRIMARY`)
where (((`id` > _binary'8991'))
and ((`id` < _binary'9990') or ((`id` = _binary'9990'))))
lock in share mode
// 其中 8991 是上次循环的上边界值
// 其中 9990 是步骤b中获取到的数据- 同步数据和应用增量binlog
- 若循环执行3-b时,若获取到数据则正常进入步骤3-c;
- 若循环执行3-b时未获取到数据,则说明剩余未同步的数据小于--chunk-size的值,执行以下SQL确认本次的上边界值
select /* gh-ost `test`.`t1` */ `id`
from (
select `id` from `test`.`t1`
where ((`id` > _binary'9990'))
and ((`id` < _binary'10000')
or ((`id` = _binary'10000')))
order by `id` asc
limit 999 ) select_osc_chunk
order by `id` desc
limit 1- 更改表名:在更改表名之前会先对表加写锁,这点需要注意。
- 会话 Cn1(这里代表一个或者多个会话): 对t1表正常执行DML操作。
- 会话 gh1 : 创建_t1_del 防止提前RENAME表,导致数据丢失。
- 会话 gh1 : 执行LOCK TABLES t1 WRITE,_t1_del WRITE。
- 会话 Cn2 : LOCK TABLES之后进来的会话操作会被阻塞。
- 会话 gh2 : 设置锁等待时间并执行RENAME
set session lock_wait_timeout:=1
rename /* gh-ost */ table `test`.`t1` to `test`.`_t1_del`, `test`.`_t1_gho` to `test`.`t1`;
// gh2 的操作因为 gh1 锁表而等待,后续有其他新发起的对表t1上的操作也会阻塞- 会话 gh1 会通过SQL 检查是否已经有会话在执行RENAME操作并且在等待MDL锁,此时会检测到gh2。
- 会话gh1 : 基于上面执行的结果,执行DROP TABLE _t1_del。
- 会话gh1 : 执行UNLOCK TABLES; 此时gh2的rename命令第一个被执行。而其他会话如Cn2的请求之后开始执行。
- 停止binlog streamer,处理收尾工作,结束。
2.2 使用限制和风险
2.2.1 使用要求和限制
- 必须有一个从库的 binlog 是 row 模式,并且 binlog_row_image 设置成full 。主节点没有特殊要求;
- 主备节点上,目标表的结构必须是相同的;
- 不支持外键约束和触发器
- 目标表上必须有主键或者唯一键,gh-ost 使用该键遍历表
- 主键或者唯一键不能包含为空的列。也就是说键中的列的属性应为 NOT NULL,或键中的列是可以为空 但是实际数据中没有 NULL 值。
- 默认情况下,若是唯一键中包含可为空的列,gh-ost 不会运行,用户可以使用 --allow-nullable-unique-key ,但是依然要确保实际数据没有NULL值,若是有 NULL 值,gh-ost 不能保证能将其完全迁移走。
- 不允许迁移存在相同名称且大小写不同的表;
- 双主复制,只支持一台实例上有写请求的情况
- 不支持表更名的操作 :ALTER TABLE ... RENAME TO some_other_name
- PXC 集群不能使用该工具。gh-ost 在更改表名阶段是使用不同的线程执行LOCK TABLE,RENME ,DROP TABLE 操作的,由于 PXC 的验证机制这会导致执行操作的 PXC 节点发生死锁。
2.2.2 使用风险
- 更改列名
FATAL gh-ost believes the ALTER statement renames columns, as follows: map[id:id_new]; as precaution, you are asked to confirm gh-ost is correct, and provide with `--approve-renamed-columns`, and we're all happy. Or you can skip renamed columns via `--skip-renamed-columns`, in which case column data may be lost- approve-renamed-columns:该选项是告诉gh-ost要同步重命名列的数据
- skip-renamed-columns:跳过重命名的列,也就是对于重命名的列上的数据不进行同步
FATAL No shared unique key can be found after ALTER! Bailing out- 对于外键的处理
- skip-foreign-key-checks :跳过外键的检查,这时不会对外键进行检查,最终会导致表上外键丢失或者外键引用的表不存在;
- discard-foreign-keys :该选项明确告诉gh-ost不在影子表上创建外键,最终变更后的表会丢失外键。
- 创建唯一键或者主键
- 锁争用问题
- 在开启半同步复制情况下,若设置 rpl_semi_sync_master_wait_point = AFTER_SYNC,在获取同步数据的上下边界时,有可能获取不到的最新上下边界,导致数据丢失。(github上已经有人提交相关bug,链接见文末注释1)
2.3 丰富的监控功能
三、MySQL ONLINE DDL 操作
3.1 主要工作流程
- Prepare阶段
- 创建新的临时frm文件
- 持有EXCLUSIVE-MDL锁,禁止读写
- 根据alter类型,确定执行方式(copy,online-rebuild,online-norebuild)
- 更新数据字典的内存对象
- 分配row_log对象记录增量
- 生成新的临时ibd文件
- DDL执行阶段
- 降级EXCLUSIVE-MDL锁,允许读写
- 扫描old_table的聚集索引每一条记录rec
- 遍历新表的聚集索引和二级索引,逐一处理
- 根据rec构造对应的索引项
- 将构造索引项插入sort_buffer块
- 将sort_buffer块插入新的索引
- 处理ddl执行过程中产生的增量(仅rebuild类型需要)
- commit阶段
- 升级到EXCLUSIVE-MDL锁,禁止读写
- 重做最后row_log中最后一部分增量
- 更新innodb的数据字典表
- 提交事务(刷事务的redo日志)
- 修改统计信息
- rename临时idb文件,frm文件
- 变更完成
3.2 使用限制和风险
3.2.1 使用限制
3.2.2 使用风险
- 由于 MySQL 的 DDL 操作无法限制同步数据的速度,所以对于较大表的操作会造成主从同步的严重延迟,和数据库的负载升高;
- 部分 DDL 会阻塞 DML 操作,导致对应操作堵塞;
- DDL 操作期间会记录临时日志,该日志文件存储在 DDL 操作期间在表上的插入、更新或删除的数据。临时日志文件在需要时根据 innodb_sort_buffer_size 的值进行扩展,直到扩展到innodb_online_alter_log_max_size 指定的值大小。如果临时日志文件超过大小上限,则 ALTER TABLE 操作将失败,并且所有未提交的并发 DML 操作都将回滚;
- DDL 操作无法直接监控数据库的负载,并且回滚DDL操作的代价更高,所以对于大表的 DDL 操作若不是采用 no-rebuild 方式,不建议直接操作;
- 若 DDL 操作需要很长时间,并且并发 DML 对表的修改量很大,以至于临时在线日志的大小超过了 innodb_online_alter_log_max_size 配置选项的值, 这种情况会导致 DB_ONLINE_LOG_TOO_BIG 错误,导致操作失败。未提交的并发DML操作将回滚。较大的innodb_online_alter_log_max_size设置允许在线DDL操作期间使用更多的 DML ,但也会延长锁定表以应用日志DML时DDL操作结束的时间;若是使用 pt-osc 或者 gh-ost 是不需要记录这种日志的,所以不会存在这种风险;
- 某些并发 DML 对原始表是允许更改的,但新表上可能不允许。而且这种操作仅在最后阶段发现失败。例如,可能会在创建唯一索引时将重复值插入到列中,或者可能会在列上创建主键索引时将 NULL 值插入到列中, 这些 DML 在原始表上能执行成功,但是在 DDL 应用日志阶段无法应用这些变更,这会导致 DDL 回滚。这种情况若是使用 pt-osc 或者 gh-ost 可能更早的发现问题,因为发生在原表的 DML 操作,会及时的在影子表上回放,若出现错误会立刻停止,不至于到最后阶段才发现;
- DDL 操作在集群中的从节点回放时,不能和其他的 DDL 和 DML 并行回放,也就是说此时并行复制失效的。而 DDL 操作一般耗时都比较久,这时就会导致集群节点不一致,对于 PXC 或者 MGR 集群严重的可能会导致流控,影响线上服务。
四、总结
- 在使用 pt-online-schema-change、gh-ost 或者直接在 MySQL 做 DDL 操作,都要关注数据库主机的磁盘空间问题,对于 pt-online-schema-change、gh-ost 会创建表的副本并拷贝数据,所以需要主机空余表空间大于表的大小已经产生的 binlog 大小才相对安全;对于 rebuild 方式的DDL 同样额外需要表的大小以上的剩余空间;
- 无论哪种方式对数据库都会产生一定的压力,都会将表上的 DML 操作重放到影子表上,所以在表频繁的 DML 时是不建议对表进行 DDL 操作;
- 对于某些 DDL 操作,MySQL 采用的是 no-rebuild 的方式,只更改表的元数据信息即可,这类DDL操作建议直接在数据库上操作,所以在创建表伊始,可以创建一些冗余字段,在实际需要时更改列名就行了;
- 对于表的 DDL 变更都会涉及 drop 旧表的操作,所以在使用 pt-online-schema-change、gh-ost 进行较大表的变更时,最好使用对应选项不自动删除旧表,而是自己选择合适的时间合适的方式再进行删除;
- 对于主从集群,在需要对大表 DDL 操作时,不建议直接操作,因为这样会导致主从延迟很严重,若是在从库执行 DDL 操作时主库挂掉,会导致无法及时切换,甚至数据丢失;
- 在 PXC 集群和 MGR 集群上不要直接执行 DDL 操作,因为很多 DDL 操作耗时都比较长可能会导致集群限流,从而导致集群无法进行对外服务。
- https://github.com/github/gh-ost/issues/1039
- https://dev.mysql.com/doc/refman/5.7/en/innodb-online-ddl-operations.html
- MySQL运维内参
- https://cloud.tencent.com/developer/article/1006513
边栏推荐
- SQL function TO_DATE (2)
- C#/VB.NET 从PDF中提取表格
- Tencent Cloud Hosting Security x Lightweight Application Server | Powerful Joint Hosting Security Pratt & Whitney Version Released
- MLX90640 红外热成像仪测温模块开发笔记(完整篇)
- 重保特辑|筑牢第一道防线,云防火墙攻防演练最佳实践
- The elder brother of the goldfish RHCA memoirs: CL210 experiment management it network - chapter
- How to record and analyze your alchemy process - use notes of the visual artifact Wandb [1]
- MySQL数据库————流程控制
- GZIPOutputStream 类源码分析
- Heavy cover special | build the first line of defense, cloud firewall offensive and defensive drills best practices
猜你喜欢

在全志V853开发板试编译QT测试

How to install voice pack in Win11?Win11 Voice Pack Installation Tutorial

Website construction process

三种方案解决:npm WARN config global --global, --local are deprecated. Use --location=global instead.

MySQL database - stored procedures and functions

力扣刷题之求两数之和

暑假第二周总结博客

Source code analysis of GZIPOutputStream class

MySQL数据库————存储过程和函数

哈哈!一个 print 函数,还挺会玩啊!
随机推荐
Prometheus's Recording rules practice
Source code analysis of GZIPOutputStream class
三种方案解决:npm WARN config global --global, --local are deprecated. Use --location=global instead.
腾讯云主机安全 x 轻量应用服务器|强强联合主机安全普惠版重磅发布
ExcelPatternTool: Excel form-database mutual import tool
Redis的内存淘汰策略和过期删除策略的区别是什么
英国伦敦大学|眼科强化学习:潜在应用和实施挑战
modbus bus module DAM-8082
483-82(23、239、450、113)
kubernetes - deploy nfs storage class
暑假第一周总结博客
网站建设流程
C#/VB.NET:从 PDF 文档中提取所有表格
TestNG multiple xml for automated testing
LeetCode 1374.生成每种字符都是奇数个的字符串
[Neural Network] This article will take you to easily analyze the neural network (with an example of spoofing your girlfriend)
屏:全贴合工艺之GFF、OGS、Oncell、Incell
如何记录分析你的炼丹流程—可视化神器Wandb使用笔记【1】
首篇 NLP 领域图神经网络综述:127 页,从图构建到实际应用面面观
[Server data recovery] Data recovery case of offline multiple disks in mdisk group of server Raid5 array