MySQL OSC(在线更改表结构)原理
1 OSC介绍
在我们的数据库操作中,更改表结构是一个常见的操作,而当我们的表数据量非常大时,我们更改表结构的时间是非
常的长,并且在跟改期间,会生成一个互斥锁,阻塞对整个表的所有操作,这样,对于我们线上数据来说是无法容忍
的,以往的做法中,为了不影响线上业务,我们一般采用:先在线下从库更改表结构,然后替换线上从库,这样一台
台的修改,最后做一下主库切换,这个过程会耗费很长时间,并且在做主库切换时,风险也非常的大,OSC(Online
Schema Change)大多都是利用了触发器的原理,实现了在线更改表结构的同时,避免了锁表,同时还允许其他的dml操
作,目前已经有多种工具实现了 OSC 下面就几种常见的的工具。
2 MySQL5.6 OnlineDDL
MySQL5.6 Online DDL可以做到DDL\DML\SELECT同时进行
示例
alter table test add name varchar(10),ALGORITHM=INPLACE ,LOCK=NONE;
#Locking Options for Online DDL
LOCK=DEFAULT
LOCK=NONE
LOCK=SHARED
LOCK=EXCLUSIVE
#Performance of In-Place versus Table-Copying DDL Operations
ALGORITHM=DEFAULT
ALGORITHM=INPLACE
ALGORITHM=COPY
实现细节
#Prepare阶段
1.创建临时frm文件
2.持有EXCLUSIVE-MDL锁,禁止读写
3.根据ALTER类型,确定执行方式(copy,online-rebuild,online-norebuild)
4.更新数据字典的内存对象
5.分配row_log对象记录增量
6.生成临时ibd文件
#ddl执行阶段
1.降级EXCLUSIVE-MDL锁,允许读写
2.扫描原表的聚簇索引每条记录
3.遍历新表的聚簇索引和二级索引,逐一处理
4.根据记录构造对应的索引项
5.将构造索引项插入sort_buffer块
6.将sort_buffer块插入新的索引
7.处理ddl执行过程中产生的增量(仅rebuild类型需要)
#commit阶段
1.升级到EXCLUSIVE-MDL锁,禁止读写
2.应用最后row_log中产的日志
3.更新innodb的数据字典表
4.提交事务(刷事务的redo日志)
5.修改统计信息
6.rename临时idb文件,frm文件
7.变更完成
从上面可以看到 在开始 和 结束阶段 还是锁表了 只是缩短了锁表的时间
以加索引为例,介绍 copy方式跟inplace方式的实现流程
#copy方式
1.新建带索引(主键索引)的临时表
2.锁原表,禁止DML,允许查询
3.将原表数据拷贝到临时表
4.禁止读写,进行rename,升级字典锁
5.完成创建索引操作
#inplace方式
1.创建索引(二级索引)数据字典
2.加共享表锁,禁止DML,允许查询
3.读取聚簇索引,构造新的索引项,排序并插入新索引
4.等待打开当前表的所有只读事务提交
5.创建索引结束
3 Percona公司的pt-online-schema-change
示例
pt-online-schema-change h=*,u=* p=**,P=* ,D=enk,t=my1 --alter "add is_sign_1 int(11) unsigned NOT NULL DEFAULT '0'" --drop-old-table [--sleep 10] --print --executeD=lots,t=t_o_tr
实现细节
1. 新建tmp_table,表结构同原表
CREATE TABLE `$db`.`$tmp_tbl` LIKE `$db`.`$tbl`"
2. 在tmp_table上更改表结构为需要的表结构
3. 在原表上建立三个触发器,如下:
#delete 触发器
CREATE TRIGGER mk_osc_del AFTER DELETE ON $table " "FOR EACH ROW "
"DELETE IGNORE FROM $new_table ""WHERE $new_table.$chunk_column = OLD.$chunk_column";
#insert 触发器
CREATE TRIGGER mk_osc_ins AFTER INSERT ON $table " "FOR EACH ROW "
"REPLACE INTO $new_table ($columns) " "VALUES($new_values)";
#update 触发器
CREATE TRIGGER mk_osc_upd AFTER UPDATE ON $table " "FOR EACH ROW "
"REPLACE INTO $new_table ($columns) " "VALUES ($new_values)";
#我们可以看到这三个触发器分别对应于INSERT、UPDATE、DELETE三种操作,
mk_osc_del,DELETE操作,我们注意到DELETE IGNORE,当新有数据时,我们才进行操作,也就是说,当在后续导入过程中,如果删除
的这个数据还未导入到新表,那么我们可以不在新表执行操作,因为在以后的导入过程中,原表中改行数据已经被删除,已经没有数据,那
么他也就不会导入到新表中;
mk_osc_ins,INSERT操作,所有的INSERT INTO全部转换为REPLACE INTO,为了确保数据的一致性,当有新数据插入到原表时,如果
触发器还未把原表数据未同步到新表,这条数据已经被导入到新表了,那么我们就可以利用replace into进行覆盖,这样数据也是一致的。
mk_osc_upd UPDATE操作,所有的UPDATE也转换为REPLACE INTO,因为当跟新的数据的行还未同步到新表时,新表是不存在这条记录
的,那么我们就只能插入该条数据,如果已经同步到新表了,那么也可以进行覆盖插入,所有数据与原表也是一致的;
#我们也能看出上述的精髓也就这这几条replace into操作,正是因为这几条replace into才能保证数据的一致性
4. 拷贝原表数据到临时表中,在脚本中使用如下语句
INSERT IGNORE INTO $to_table ($columns) " "SELECT $columns FROM $from_table " "WHERE ($chunks->[$chunkno])",
我们能看到他是通过一些查询(基本为主键、唯一键值)分批把数据导入到新的表中,在导入前,我们能通过参数--chunk-size对每次
导入行数进行控制,已减少对原表的锁定时间,并且在导入时,我们能通过—sleep参数控制,在每个chunk导入后与下一次chunk导入开
始前sleep一会,sleep时间越长,对于磁盘IO的冲击就越小
5. Rename 原表到old表中,在把临时表Rename为原表
RENAME TABLE `$db`.`$tmp_tbl` TO `$db`.`$tbl` ;
在rename过程,其实我们还是会导致写入读取堵塞的,所以从严格意思上说,我们的OSC也不是对线上环境没有一点影响,但由于
rename操作只是一个修改名字的过程,也只会修改一些表的信息,基本是瞬间结束,故对线上影响不太大
6. 清理以上过程中的不再使用的数据,如OLD表
以上即为整个Percona OSC的过程,我们看到精华部分就触发器那一块,不过还有很多细节我未介绍,如:外键、记录binlog(默认情况是不记录binlog的)等等
由于环境的复杂性,此工具还是有很多风险,如以下几个方面问题或者需要规避的一些问题:
1. 此工具不是原子操作,如果某一点失败,不仅仅会留下很多中间过程的垃圾文件,而这些文件很难完全清理,并且如果有这些文件存在,
那么就不能在次执行OSC操作;
2.在执行时,尽量避免有这个表的批量更新、锁表、优化表的操作,我们能想象的到,如果有锁表、优化表那么OSC是否还能正常执行?
3.如果存在主从结构,那么尽量在从库先执行,因为如果在主库执行完毕后在到从库执行,我们能想象,主库字段多同步到从库,会不会有问题呢?
4.必须是单一列的主键或者单一唯一键,这样我们在insert select *from分片时,是不是能更好的处理量呢?
5.不要有外键,尽管脚本经过严格测试,但是是否还有bug,也未知,表的外键是不是会带来更多的问题呢?
6.在执行之前,我们是不是要对磁盘容量进行评估呢?因为OSC会使用表的一倍以上空间。
**以上列到的,只是部分问题,我想如果需要在线进行实施,还需要经过严格的测试,但是它的实现为我们提供了一个很好的在线更改表结构 方法,我相信只要我们能很好的规避他的弊端,它会给我们带来很大的帮助;
**
PS:
使用 pt-osc 修改主键时注意:
原表上有个复合主键,现在要添加一个自增 id 作为主键,如何进行?
会涉及到以下修改动作:
1.删除复合主键定义
2.添加新的自增主键 3.原复合主键字段,修改成唯一索引
需要将删除原主键、增加新主键和增加原主键为唯一键同时操作:
alter "DROP PRIMARY KEY,add column pk int auto_increment primary key,add unique key uk_id_k(id,k)
3.4 OAK Openark – kit
openark kit 提供一组小程序,用来帮助日常的 MySQL 维护任务,可代替繁杂的手工操作。
oak-apply-ri: apply referential integrity on two columns with parent-child relationship.
oak-block-account: block or release MySQL users accounts, disabling them or enabling them to login.
oak-chunk-update: Perform long, non-blocking UPDATE/DELETE operation in auto managed small chunks.
oak-kill-slow-queries: terminate long running queries.
oak-modify-charset: change the character set (and collation) of a textual column.
oak-online-alter-table: Perform a non-blocking ALTER TABLE operation.
oak-purge-master-logs: purge master logs, depending on the state of replicating slaves.
oak-security-audit: audit accounts, passwords, privileges and other security settings.
oak-show-limits: show AUTO_INCREMENT “free space”.
oak-show-replication-status: show how far behind are replicating slaves on a given master.
示例
python oak-online-alter-table -u root --ask-pass -S /u01/mysql/my3306/run/mysql.sock -d replTestDB -t sbtest1 -g new_sbtest1 -a "add last_update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,add key last_update_time(last_update_time)" --sleep=300 --skip-delete-pass
实现细节
# 1.确认该表是否符合 oak-online-alter-table 的执行条件:
已有触发器? 检查触发器->备份触发器->删除触发器
SELECT TRIGGER_SCHEMA,TRIGGER_NAME,EVENT_OBJECT_SCHEMA,
EVENT_OBJECT_TABLE
FROM information_schema.TRIGGERS
WHERE event_object_schema = 'replTestDB';
Select * from information_schema.key_column_usage where
Referenced_table_schema='replTestDB' and
Referenced_table_name='sbtest1';
#2.执行 oak 命令,至于执行时间:
如果表有 1亿,大概要执行 12 个小时,就需要在一周业务量少的时候执行
cd /u01/tools/openark-kit-196/scripts/
python oak-online-alter-table -u root --ask-pass -S /u01/mysql/my3306/run/mysql.sock -d replTestDB -t sbtest1 -g new_sbtest1 -a "add last_update_time timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,add key last_update_time(last_update_time)" --sleep=300 --skip-delete-pass
#3.Online DDL 之后要进行数据一致性校验:如果 DDL 改变了表字段类型,可能导致表数据变化
#4.表切换
rename table sbtest1 to old_sbtest1,new_sbtest1 to sbtest1;
#5. 删除触发器
drop trigger sbtest1_AI_oak;
drop trigger sbtest1_AU_oak;
drop trigger sbtest1_AD_oak;
#6.删除表
MySQL OSC(在线更改表结构)原理的更多相关文章
- gh-ost 号称是不需要触发器(Triggerless)支持的在线更改表结构的工具
https://segmentfault.com/a/1190000006158503?utm_source=tuicool&utm_medium=referral
- [linux][mysql] 命令更改表结构:添加、删除、修改字段、调整字段顺序
原文出处:http://www.phpernote.com/MySQL/1120.html 查看表结构: desc tabl_name; show columns fromtable_name: 常用 ...
- (转)pt-online-schema-change在线修改表结构
原文:http://www.ywnds.com/?p=4442 一.背景 MySQL大字段的DDL操作:加减字段.索引.修改字段属性等,在5.1之前都是非常耗时耗力的,特别是会对MySQL服务产生影响 ...
- mysql在线修改表结构大数据表的风险与解决办法归纳
整理这篇文章的缘由: 互联网应用会频繁加功能,修改需求.那么表结构也会经常修改,加字段,加索引.在线直接在生产环境的表中修改表结构,对用户使用网站是有影响. 以前我一直为这个问题头痛.当然那个时候不需 ...
- 数据库遇到的问题——mysql在线修改表结构大数据表的风险与解决办法归纳
互联网应用会频繁加功能,修改需求.那么表结构也会经常修改,加字段,加索引.在线直接在生产环境的表中修改表结构,对用户使用网站是有影响. 以前我一直为这个问题头痛.当然那个时候不需要我来考虑,虽然我们没 ...
- MySQL在创建相同表结构时as和like 使用的区别
1.MySQL的复制相同表结构方法: 1)create table table_name as select * from table1 where 1=2 (或者limit 0): 2) crea ...
- 【工具篇】利用DBExportDoc V1.0 For MySQL自动生成数据库表结构文档
对于DBA或开发来说,如何规范化你的数据库表结构文档是灰常之重要的一件事情.但是当你的库,你的表排山倒海滴多的时候,你就会很头疼了. 推荐一款工具DBExportDoc V1.0 For MySQL( ...
- MSSQL 更改表结构
更改表结构: alter TABLE 表1 ALTER COLUMN 列名1 NCHAR(40)
- mysql导出word的表结构操作
mysql导出word的表结构操作 1.首先准备好mysql的相关插件mysql-connector-odbc和DBExportDoc 百度网盘地址: 链接:https://pan.baidu.com ...
随机推荐
- ios上传图片遇见了一个TimeoutError(DOM Exception 23)异常
TimeoutError(DOM Exception 23):The operation timed out 百度了下,没发现解决办法
- CollectionUtils工具类使用指南
CollectionUtils提供很多对集合的操作方法,常用的方法如下:(参考文章:http://www.open-open.com/code/view/1420470842125) import o ...
- List列表按照对象进行排序
在某些业务场景下需要根据list列表中对象的字段进行排序.今天就以实例说明: 实体类 public class Product { private int discount; // 省略getter/ ...
- Go标准容器之List
简介Go的标准包container中包含了常用的容器类型,包括conatiner/list,container/heap,container/ring.本篇介绍conatiner/list. cona ...
- 动态加载css、js引用
在js代码中动态的加载js.css文件的引用 function addJsCssByLink(type,url) { var doc=document; if(type="js") ...
- Python之functools库
functools库用于高阶函数,指那些作用于函数或者返回其他函数的函数 functools提供方法如下: cmp_to_key 将一个比较函数转换关键字函数 partial 针对函数起作用,并且是部 ...
- iPhone图片拉伸:resizableImageWithCapInsets
1 [[UIImage imageNamed:@"button_textured_30"] resizableImageWithCapInsets:UIEdgeInsetsMake ...
- Bootstrap-table学习笔记(一)
第一次使用Bootstrap-table这个表格插件,记录一下使用过程中遇到的问题. =================== | 引入CSS文件 <link rel="styleshe ...
- 【java规则引擎】drools6.5.0版本中kmodule.xml解析
kmodule.xml文件存放在src/main/resources/META-INF/文件夹下. <?xml version="1.0" encoding="UT ...
- 洛谷P2661 信息传递
传送门 题目大意:每个人每一轮可以把消息传给另一个人,问几轮后某个人可以从人 听到自己的消息. 题解:tarjian缩点,求缩点后缩的点包含的最少的点个数. 代码: 正解 #include<io ...