MySQL中给一张千万甚至更大量级的表添加字段一直是比较头疼的问题,遇到此情况通常该如果处理?本文通过常见的三种场景进行案例说明。

1、 环境准备

数据库版本: 5.7.25-28(Percona 分支)

服务器配置:  3台centos 7虚拟机,配置均为2CPU  2G内存

数据库架构: 1主2从的MHA架构(为了方便主从切换场景的演示,如开启GTID,则两节点即可),关于MHA搭建可参考此文 MySQL高可用之MHA集群部署

准备测试表:  创建一张2kw记录的表,快速创建的方法可以参考快速创建连续数

本次对存储过程稍作修改,多添加几个字段,存储过程如下:

DELIMITER $$
CREATE PROCEDURE `sp_createNum`(cnt INT )
BEGIN
DECLARE i INT DEFAULT 1;
DROP TABLE if exists tb_add_columns;
CREATE TABLE if not exists tb_add_columns(id int primary key,col1 int,col2 varchar(32));
INSERT INTO tb_add_columns(id,col1,col2) SELECT i as id ,i%7 as col1,md5(i) as col2; WHILE i < cnt DO
BEGIN
INSERT INTO tb_add_columns(id,col1,col2) SELECT id + i as id ,( id + i) %7 as col1,md5( id + i) as col2 FROM tb_add_columns WHERE id <=cnt - i ;
SET i = i*2;
END;
END WHILE;
END$$
DELIMITER ;

调用存储过程,完成测试表及测试数据的创建。

mysql> call sp_createNum(20000000);

2.  直接添加字段

使用场景: 在系统不繁忙或者该表访问不多的情况下,如符合ONLINE DDL的情况下,可以直接添加。

模拟场景: 创建一个测试脚本,每10s访问该表随机一条记录,然后给该表添加字段

访问脚本如下

#!/bin/bash
# gjc for i in {..} # 访问次数1000000000,按需调整即可
do
id=$RANDOM #生成随机数
mysql -uroot -p'' --socket=/data/mysql3306/tmp/mysql.sock -e "select a.*,now() from testdb.tb_add_columns a where id = "$id # 访问数据
sleep # 暂停10s
done

运行脚本

sh  test.sh

给表添加字段

mysql> alter table  testdb.tb_add_columns add col3 int;

此时,访问正常。

附ONLINE DDL的场景如下,建议DBA们必须弄清楚

(图片转载于https://blog.csdn.net/finalkof1983/article/details/88355314)

(图片转载于https://blog.csdn.net/finalkof1983/article/details/88355314)

3.   使用工具在线添加

虽然Online DDL添加字段时,表依旧可以读写,但是生产环境使用场景中对大表操作使用最多的还是使用工具pt-osc或gh-ost添加。

本文主要介绍 pt-osc(pt-online-schema-change) 来添加字段,该命令是Percona Toolkit工具中的使用频率最高的一种

关于Percona Toolkit的安装及主要使用可以参考  五分钟学会Percona Toolkit 安装及使用

添加字段

root@mha1 ~]# pt-online-schema-change --alter "ADD COLUMN  col4  int" h=localhost,P=,p=,u=root,D=testdb,t=tb_add_columns,S=/data/mysql3306/tmp/mysql.sock  --charset=utf8mb4 --execute

主要过程如下:

> Cannot connect to A=utf8mb4,P=,S=/data/mysql3306/tmp/mysql.sock,h=192.168.28.132,p=...,u=root
> Cannot connect to A=utf8mb4,P=,S=/data/mysql3306/tmp/mysql.sock,h=192.168.28.131,p=...,u=root
No slaves found. See --recursion-method if host mha1 has slaves. # 因为使用的是socket方式连接数据库 且未配置root远程连接账号,所以会有此提示 # A software update is available:
Operation, tries, wait:
analyze_table, , 1
copy_rows, , 0.25
create_triggers, , 1                    
drop_triggers, ,
swap_tables, ,
update_foreign_keys, ,
Altering `testdb`.`tb_add_columns`...
Creating new table... # 创建中间表,表名为"_原表名_new"
Created new table testdb._tb_add_columns_new OK.
Altering new table... # 修改表,也就是在新表上添加字段,因新表无数据,因此很快加完
Altered `testdb`.`_tb_add_columns_new` OK.
--20T12:: Creating triggers... # 创建触发器,用于在原表拷贝到新表的过程中原表有数据的变动(新增、修改、删除)时,也会自动同步至新表中
--20T12:: Created triggers OK.
--20T12:: Copying approximately rows... # 拷贝数据,数据库量是统计信息里的,不准确
Copying `testdb`.`tb_add_columns`: % : remain # 分批拷贝数据(根据表的size切分每批拷贝多少数据),拷贝过程中可以用show processlist看到对应的sql
Copying `testdb`.`tb_add_columns`: % : remain
Copying `testdb`.`tb_add_columns`: % : remain
Copying `testdb`.`tb_add_columns`: % : remain
Copying `testdb`.`tb_add_columns`: % : remain
Copying `testdb`.`tb_add_columns`: % : remain
Copying `testdb`.`tb_add_columns`: % : remain
Copying `testdb`.`tb_add_columns`: % : remain
Copying `testdb`.`tb_add_columns`: % : remain
--20T12:: Copied rows OK. # 拷贝数据完成
--20T12:: Analyzing new table... # 优化新表
--20T12:: Swapping tables... # 交换表名,将原表改为"_原表名_old",然后把新表表名改为原表名
--20T12:: Swapped original and new tables OK.
--20T12:: Dropping old table... # 删除旧表(也可以添加参数不删除旧表)
--20T12:: Dropped old table `testdb`.`_tb_add_columns_old` OK.
--20T12:: Dropping triggers... # 删除触发器
--20T12:: Dropped triggers OK.
Successfully altered `testdb`.`tb_add_columns`. # 完成

修改过程中,读写均不受影响,大家可以写个程序包含读写的

注:  无论是直接添加字段还是用pt-osc添加字段,首先都得拿到该表的元数据锁,然后才能添加(包括pt-osc在创建触发器和最后交换表名时都涉及),因此,如果一张表是热表,读写特别频繁或者添加时被其他会话占用,则无法添加。

例如: 锁住一条记录

用pt-osc添加字段,会发现一直卡在创建触发器那一步

此时查看对应的SQL正在等待获取元数据锁

换成直接添加也一样,例如

当达到锁等待后将会报错放弃添加字段

mysql> alter table  testdb.tb_add_columns add col5 int;
ERROR 1205 (HY000): Lock wait timeout exceeded; try restarting transaction

对于此情况,需等待系统不繁忙情况下添加,或者使用后续的在从库创建再进行主从切换

4  先在从库修改,再进行主从切换

使用场景: 如果遇到上例中一张表数据量大且是热表(读写特别频繁),则可以考虑先在从库添加,再进行主从切换,切换后再将其他几个节点上添加字段。

先在从库添加(本文在备选节点添加)

mysql> alter table  testdb.tb_add_columns add col5 int;
Query OK, 0 rows affected (1 min 1.91 sec)
Records: 0 Duplicates: 0 Warnings: 0

进行主从切换

使用MHA脚本进行在线切换

masterha_master_switch  --conf=/etc/masterha/app1.conf --master_state=alive  --orig_master_is_new_slave --new_master_host=192.168.28.131  --new_master_port=

切换完成后再对其他节点添加字段

/* 原主库上添加192.168.28.128  */
mysql> alter table testdb.tb_add_columns add col5 int;
Query OK, 0 rows affected (1 min 8.36 sec)
Records: 0 Duplicates: 0 Warnings: 0 /* 另一个从库上添加192.168.28.132 */
mysql> alter table testdb.tb_add_columns add col5 int;
Query OK, 0 rows affected (1 min 8.64 sec)
Records: 0 Duplicates: 0 Warnings: 0

这样就完成了字段添加。

5.  小结

生产环境MySQL添加或修改字段主要通过如下三种方式进行,实际使用中还有很多注意事项,大家要多多总结。

  • 直接添加

如果该表读写不频繁,数据量较小(通常1G以内或百万以内),直接添加即可(可以了解一下online ddl的知识)

  • 使用pt_osc添加

如果表较大 但是读写不是太大,且想尽量不影响原表的读写,可以用percona tools进行添加,相当于新建一张添加了字段的新表,再降原表的数据复制到新表中,复制历史数据期间的数据也会同步至新表,最后删除原表,将新表重命名为原表表名,实现字段添加

  • 先在从库添加 再进行主从切换

如果一张表数据量大且是热表(读写特别频繁),则可以考虑先在从库添加,再进行主从切换,切换后再将其他几个节点上添加字段

 

mysql大表在不停机的情况下增加字段该怎么处理的更多相关文章

  1. [记录]一则清理MySQL大表以释放磁盘空间的案例

    一则清理MySQL大表以释放磁盘空间的案例 一.基本情况: 1.dbtest库554G,先清理st_online_time_away_ds(37G)表的数据,保留半年的数据: 1)删除的数据:sele ...

  2. 优秀后端架构师必会知识:史上最全MySQL大表优化方案总结

    本文原作者“ manong”,原创发表于segmentfault,原文链接:segmentfault.com/a/1190000006158186 1.引言   MySQL作为开源技术的代表作之一,是 ...

  3. MySQL 大表优化方案(长文)

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  4. 从云数据迁移服务看MySQL大表抽取模式

    摘要:MySQL JDBC抽取到底应该采用什么样的方式,且听小编给你娓娓道来. 小编最近在云上的一个迁移项目中被MySQL抽取模式折磨的很惨.一开始爆内存被客户怼,再后来迁移效率低下再被怼.MySQL ...

  5. mysql大表如何优化

    作者:哈哈链接:http://www.zhihu.com/question/19719997/answer/81930332来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业转载请注明出处 ...

  6. 详解MySQL大表优化方案( 转)

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  7. MySQL 大表优化方案探讨

    当MySQL单表记录数过大时,增删改查性能都会急剧下降,可以参考以下步骤来优化: 单表优化 除非单表数据未来会一直不断上涨,否则不要一开始就考虑拆分,拆分会带来逻辑.部署.运维的各种复杂度,一般以整型 ...

  8. Mysql大表查询优化技巧总结及案例分析

    http://www.169it.com/article/3219955334.html     sql语句使用基本原则:1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 orde ...

  9. MySQL大表优化方案

    转:https://segmentfault.com/a/1190000006158186?hmsr=toutiao.io&utm_medium=toutiao.io&utm_sour ...

随机推荐

  1. protocbuf的简单理解

    之前通信协议替换为protocbuf!新老交替,很多不同看法,也提出来一些负面因数: 1.老的内部通信协议体已经有一段时间了,稳定熟悉! 2.通过通信结构体进行交互,实际上并没有序列化和反序列化的过程 ...

  2. 【C++】运算符优先级和结合性

    从上表可以大致总结这样一个规律: 优先级 运算符 高 初等运算符 | 单目运算符 | 算术运算符(先乘除,后加减) | 关系运算符 | 逻辑运算符(不包括!) | 条件运算符 ↓ 赋值运算符 低 逗号 ...

  3. [JavaWeb基础] 011.Struts2 配置拦截器

    在网页开发中有一个很重要的东西就是拦截器,就是在请求接收到的时候先到拦截器中进行一些逻辑处理,例如会话是否过期的验证等.在Struts2中我们可以编写一个拦截器的类,然后在struts.xml中简单配 ...

  4. MRCTF 2020 WP

    MRCTF 2020 WP 引言 周末趁上课之余,做了一下北邮的CTF,这里记录一下做出来的几题的WP ez_bypass 知识点:MD5强类型比较,is_numeric()函数绕过 题目源码: I ...

  5. Rocket - util - AsyncQueue

    https://mp.weixin.qq.com/s/6McbqOKM4fu4J5vdpZvxKw   简单介绍异步队列(AsyncQueue)的实现.   ​​ 0. 异步队列   异步队列的两端分 ...

  6. Java实现 蓝桥杯 算法提高 菱形

    试题 算法提高 菱形 请编程输出一个菱形.输入为菱形中心到顶点的距离 样例输入 2 样例输出 import java.util.Scanner; public class Main { public ...

  7. Java实现 LeetCode 667 优美的排列 II(暴力)

    667. 优美的排列 II 给定两个整数 n 和 k,你需要实现一个数组,这个数组包含从 1 到 n 的 n 个不同整数,同时满足以下条件: ① 如果这个数组是 [a1, a2, a3, - , an ...

  8. Java实现蓝桥杯历届试题格子刷油漆

    问题描述 X国的一段古城墙的顶端可以看成 2*N个格子组成的矩形(如下图所示),现需要把这些格子刷上保护漆. 你可以从任意一个格子刷起,刷完一格,可以移动到和它相邻的格子(对角相邻也算数),但不能移动 ...

  9. Java中多态举例说明

    这里我也就大概说一下他们的关系, 接口就是动物,然而每一个类就是一种动物 给猫有两个功能:叫和睡觉 狗:叫 在f方法里面可以把猫的功能实现 但不能实现狗的功能 在主方法里面有一个猫有一个狗 分别调用 ...

  10. java实现第五届蓝桥杯殖民地

    殖民地 带着殖民扩张的野心,Pear和他的星际舰队登上X星球的某平原.为了评估这块土地的潜在价值,Pear把它划分成了M*N格,每个格子上用一个整数(可正可负)表示它的价值. Pear要做的事很简单- ...