文章目录
- 一、环境调研
- 1.1 服务器信息
- 1.2 数据库信息
- 1.2.1 数据库基本信息
- 1.2.2 数据库用户信息
- 1.3 库表信息调研
- 1.3.1 数据库对象统计概览
- 1.3.2 数据库信息统计概览
- 1.3.3 表信息统计概览
- 1.3.4 忽略大小写敏感后是否存在重名表
- 1.4 数据流拓扑
- 二、方案制定
- 2.1 各种方案特点对比
- 2.2 数据库账号权限
- 2.3 迁移步骤/进度记录
- 2.4 数据迁移中的一些关注点
- 三、迁移演练
- 3.1 迁移演练记录
- 3.2 迁移方案优化
- 四、迁移割接/业务验证/回滚
- 4.1 数据验证脚本
当我们面对一个未知的数据库业务系统,并需要在一定时间内完成整套业务(应用+数据库)的迁移,前期的环境调研、迁移方案制定、迁移测试演练、正式迁移割接、迁移回滚方案,每一步都是重中之重必不可少,本文档主要梳理整个迁移过程中一些通用关注点,可帮助大家快速上手数据库迁移。
一、环境调研
无论我们迁移什么数据库,我们要做的第一步都是先要了解清楚我们要迁移的数据库的基本环境信息,以及它的使用情况,在整个业务拓扑的流向。这样我们才能尽量在第一次迁移测试时少走一些弯路。
1.1 服务器信息
所属业务系统 | 服务器名称 | 服务器IP | 服务器配置 | 存储配置 | 数据库IP:port | 数据库版本 |
xxx_生产 | xxx_prod | 172.16.xx.xx | 16C128G | SSD 2T | 172.16.xx.xx:3306 | MySQL 5.7.33 |
xxx_预发 | xxx_stg |
1.2 数据库信息
1.2.1 数据库基本信息
若MySQL迁移为自建环境,那么我们最好使用源环境的my.cnf文件对数据库进行初始化并应用,但是如果我们需要将自建数据库迁移至一些云数据库环境中,某些影响数据库使用的关键参数会影响到我们的迁移方案制定以及目标环境的正常业务使用,我们还是需要重点关注以下这些参数:
数据库版本 | 事物隔离级别 | 自动提交 | Server_id | 是否开启binlog | binlog模式 | binlog镜像模式 | 大小写敏感 | 索引前缀上限 | sql_mode |
5.7.20-log | READ-COMMITTED | 1 | 33061 | 1 | ROW | FULL | 0 | 1 | STRICT_TRANS_TABLES,NO_ZERO_IN_DATE |
相关SQL支持:
mysql> SELECT version(), @@transaction_isolation, @@autocommit, @@server_id, @@log_bin, @@binlog_format, @@binlog_row_image, @@lower_case_table_names, @@innodb_large_prefix, @@sql_mode;
1.2.2 数据库用户信息
数据库账号 | 权限 | 所属业务 |
monitor@% | GRANT PROCESS, REPLICATION CLIENT ON . TO ‘monitor’@’%’ | xxx_a |
root@localhost | GRANT ALL PRIVILEGES ON . TO ‘root’@‘localhost’ WITH GRANT OPTION | xxx_b |
sansi_rw@% | GRANT ALL PRIVILEGES ON . TO ‘sansi_rw’@’%’ | xxx_c |
相关SQL支持:
-- 查看当前数据库用户列表
mysql> select user,host from mysql.user;
-- 查看制定数据库用户权限
mysql> show grants for 'root'@'localhost';
1.3 库表信息调研
1.3.1 数据库对象统计概览
数据库 | 对象类型 | 对象数量 |
db1 | EVENTS | 1 |
db1 | PROCEDURE | 2 |
db1 | TABLE | 4 |
db1 | TRIGGER | 1 |
db1 | VIEW | 2 |
1.3.2 数据库信息统计概览
数据库 | 字符集 | 校对规则 | 预估记录数 | 预估数据容量(MB) | 预估索引容量(MB) | 预估空闲空间(MB) | 预估总大小(MB) |
mysql | utf8mb4 | utf8mb4_general_ci | 1982 | 2.11 | 0.15 | 4.00 | 6.31 |
BIGDATA_ORDER | utf8mb4 | utf8mb4_general_ci | 2611 | 0.21 | 0.00 | 0.00 | 0.21 |
information_schema | utf8 | utf8_general_ci | NULL | 0.10 | 0.00 | 1230.00 | 1230.10 |
db1 | utf8 | utf8_general_ci | 9 | 0.03 | 0.00 | 0.00 | 0.03 |
AAA_bbb | utf8 | utf8_general_ci | 10 | 0.02 | 0.00 | 0.00 | 0.02 |
sys | utf8 | utf8_general_ci | 6 | 0.01 | 0.00 | 0.00 | 0.01 |
db2 | utf8 | utf8_general_ci | 4 | 0.01 | 0.00 | 0.00 | 0.01 |
performance_schema | utf8 | utf8_general_ci | 1322296 | 0.00 | 0.00 | 0.00 | 0.00 |
db3 | utf8 | utf8_general_ci | NULL | NULL | NULL | NULL | NULL |
1.3.3 表信息统计概览
数据库名 | 表名 | 引擎 | 行模式 | 是否有主键 | 预估数据容量(MB) | 预估索引容量(MB) | 预估空闲空间(MB) | 预估总大小(MB) | 预估行数 |
db1 | t2 | InnoDB | Dynamic | 无 | 0.01 | 0.00 | 0.00 | 0.01 | 8 |
db1 | AAA_bbb | InnoDB | Dynamic | 无 | 0.01 | 0.00 | 0.00 | 0.01 | 2 |
db1 | t1 | InnoDB | Dynamic | 有 | 0.18 | 0.00 | 0.00 | 0.18 | 2605 |
db1 | child | InnoDB | Dynamic | 有 | 0.01 | 0.00 | 0.00 | 0.01 | 0 |
db1 | parent | InnoDB | Dynamic | 有 | 0.01 | 0.00 | 0.00 | 0.01 | 0 |
1.3.4 忽略大小写敏感后是否存在重名表
数据 | 库表名 | 统计数 |
xx | xx | 2 |
相关SQL支持:
-- 数据库对象统计概览
select db AS '数据库',type AS '对象类型',cnt AS '对象数量' from
(select 'TABLE' type,table_schema db, COUNT(*) cnt from information_schema.`TABLES` a where table_type='BASE TABLE' group by table_schema
union all
select 'EVENTS' type,event_schema db,count(*) cnt from information_schema.`EVENTS` b group by event_schema
union all
select 'TRIGGER' type,trigger_schema db,count(*) cnt from information_schema.`TRIGGERS` c group by trigger_schema
union all
select 'PROCEDURE' type,db ,count(*) cnt from mysql.proc d where`type` = 'PROCEDURE' group by db
union all
select 'FUNCTION' type,db,count(*) cnt from mysql.proc e where`type` = 'FUNCTION' group by db
union all
select 'VIEW' type,TABLE_SCHEMA,count(*) cnt from information_schema.VIEWS f group by table_schema ) t
order by db,type;
-- 数据库信息统计概览
select a.SCHEMA_NAME AS '数据库名', a.DEFAULT_CHARACTER_SET_NAME AS '字符集', a.DEFAULT_COLLATION_NAME AS '校对规则', sum(table_rows) AS '记录数总计', sum(truncate(data_length/1024/1024, 2)) AS '预估数据容量(MB)', sum(truncate(index_length/1024/1024, 2)) AS '预估索引容量(MB)', sum(truncate(data_free/1024/1024, 2)) AS '预估空闲空间(MB)', sum(truncate((data_length+index_length+data_free)/1024/1024, 2)) AS '预估总大小(MB)' from INFORMATION_SCHEMA.SCHEMATA a left outer join information_schema.tables b on a.SCHEMA_NAME=b.TABLE_SCHEMA group by a.SCHEMA_NAME, a.DEFAULT_CHARACTER_SET_NAME,a.DEFAULT_COLLATION_NAME order by sum(data_length) desc, sum(index_length) desc;
-- 表信息统计概览
select a.table_schema AS '数据库名', a.table_name as '表名', a.ENGINE as '引擎', a.ROW_FORMAT as '行模式', case when tmp.table_name is null then '有' when tmp.table_name is not null then '无' end '是否有主键', truncate(data_length/1024/1024, 2) AS '预估数据容量(MB)', truncate(index_length/1024/1024, 2) AS '预估索引容量(MB)', truncate(data_free/1024/1024, 2) AS '预估空闲空间(MB)', truncate((data_length+index_length+data_free)/1024/1024, 2) AS '预估总大小(MB)', a.table_rows as '预估行数' from information_schema.tables a left join ( select table_schema,table_name from information_schema.tables where (table_schema,table_name) not in( select distinct table_schema,table_name from information_schema.columns where COLUMN_KEY='PRI' ) and TABLE_TYPE='BASE TABLE' and table_schema not in ('sys','mysql','information_schema','performance_schema') ) tmp on a.table_schema=tmp.table_schema and a.table_name=tmp.table_name where a.TABLE_TYPE='BASE TABLE' and a.table_schema not in ('sys','mysql','information_schema','performance_schema') order by a.table_schema,data_length desc;
-- 忽略大小写敏感后是否存在重名表
select table_schema as '数据库',lower(table_name) as '表名',count(*) as '统计数' from information_schema.tables group by table_schema,lower(table_name) having count(*) >1;
1.4 数据流拓扑
数据流拓扑可帮助我们了解在整个业务模块中,该数据库的整个数据来源以及流向。首先就拿数据源来讲,可能数据库的数据源除应用程序1、应用程序2等等,此外可能还存在一些数据周期性的同步任务,这就需要我们在整个迁移乃至割接过程中需要协调各方人员一起配合完成整个迁移工作。其次是数据流向/使用也可能是多种多样的,我们需要调研清楚这些信息,方便我们规划在整个迁移中的数据/业务验证方式。
二、方案制定
2.1 各种方案特点对比
方案 | 优势 | 劣势 | 描述 |
依赖原生复制 | 可达到数据的实时同步,割接停机窗口耗时最短;可直接对原master进行复制,也可对slave进行级联复制,MySQL 5.7以上也可实现多源复制;可设置复制对象 | master、级联复制源的slave需要开启binlog相关参数;若数据库中存在频繁对无主键表的DML,复制延迟将会很大;受限于数据库版本兼容性 | |
xtrabackup | 物理备份恢复速度相对较快,可通过全量备份+增量备份+binlog的方式缩短割接停机窗口 | 需要预留一定的割接停机窗口,物理恢复不可跨版本迁移;不可单独迁移其中某个数据库/表 | |
mysqldump | 适用于数据量相对较小,需要跨版本迁移的场景;可通过全量dump+binlog增量恢复的方式缩短割接停机窗口;迁移对象灵活 | 需要预留一定的割接停机窗口,逻辑恢复相对较慢,不适用于数据量较大的场景; | |
DTS | 比较成熟的数据同步工具,可实现全量+实时增量同步,割接停机窗口耗时最短;比较适用于自建上云使用;迁移对象灵活 | 需要开启相关binlog才可进行增量同步;对无主键表进行了一定的优化,但还是不能很好支持无主键表的频繁变更操作增量同步;依赖阿里,部分报错处理只能通过阿里进行支持 | |
Dataworks | 一种离线数据处理工具,适用于数据库异构改造迁移;迁移对象灵活 | 必须源环境停止业务后,才可进行一次性迁移处理,割接停机窗口耗时最大;表级别配置迁移任务 | |
canal | 阿里开源项目,可通过binlog实现增量实时同步;迁移对象灵活 | 与DTS相同 |
2.2 数据库账号权限
源端数据库账号权限要求:
- 物理备份用户权限:RELOAD, PROCESS, LOCK TABLES, REPLICATION CLIENT
- 原生复制用户权限:REPLICATION SLAVE
- DTS/canal:全量操作:待迁移数据库SELECT权限 ; 增量操作:待迁移数据库SELECT权限、REPLICATION SLAVE、REPLICATION CLIENT、SHOW VIEW
目标端数据库账号权限要求:迁移数据库的读写权限
2.3 迁移步骤/进度记录
迁移任务 | 迁移步骤 | 重要过程和问题 | 结果记录 | 预计执行时间 | 实际完成时间 | 执行人 | 配合方 | 是否通过 | 进度 | 描述 |
迁移任务1 | 目标环境数据库部署 | - | 已完成 | 2021-03-01 | 2021-03-01 | xxx | - | 通过 | 已完成 | |
- | 源/目标网络打通 | - | 已完成 | 2021-03-03 | 2021-03-03 | xxx | yyy | 通过 | 已完成 | |
- | 源/目标数据库账号申请 | - | 已完成 | 2021-03-03 | 2021-03-03 | xxx | yyy | 通过 | 已完成 | |
- | 连通性测试 | - | 已完成 | 2021-03-03 | 2021-03-03 | xxx | - | 通过 | 已完成 | |
- | 前置任务 | 需要业务方配置的一些业务关停等操作,根据实际编写 | 已完成 | 2021-03-05 | 2021-03-05 | yyy | xxx | 通过 | 已完成 | |
- | 数据迁移 | 迁移对象记录、迁移时间记录、迁移中问题点记录 | 已完成 | 2021-03-05 | 2021-03-05 | xxx | yyy | 通过 | 已完成 | |
- | 数据校验 | 常规count校验,业务校验 | 已完成 | 2021-03-05 | 2021-03-05 | xxx | yyy | 通过 | 已完成 | |
- | 业务割接 | - | 已完成 | 2021-03-05 | 2021-03-05 | xxx | yyy | 通过 | 已完成 | |
- | 回滚方案 | - | 已完成 | 2021-03-05 | 2021-03-05 | xxx | yyy | 通过 | 已完成 | |
迁移任务2 |
2.4 数据迁移中的一些关注点
- 评估跨版本迁移的风险性和兼容性,跨版本下业务需要提前做好测试
- 迁移源/目标中关键性参数尽量保证一致,特别是1.2.1 中提到的一些关键参数、以及数据库字符集格式等
- 在使用一些利用binlog进行数据同步迁移方案中,要考虑目标端event、trigger对数据一致性的影响
- 数据校验中,重要数据还是需要业务层进行业务校验
三、迁移演练
3.1 迁移演练记录
迁移任务 | 迁移步骤 | 重要过程和问题 | 结果记录 | 预计执行时间 | 实际完成时间 | 执行人 | 配合方 | 是否通过 | 进度 | 描述 |
迁移任务1 | 目标环境数据库部署 | - | 已完成 | 2021-03-01 | 2021-03-01 | xxx | - | 通过 | 已完成 | |
- | 源/目标网络打通 | - | 已完成 | 2021-03-03 | 2021-03-03 | xxx | yyy | 通过 | 已完成 | |
- | 源/目标数据库账号申请 | - | 已完成 | 2021-03-03 | 2021-03-03 | xxx | yyy | 通过 | 已完成 | |
- | 连通性测试 | - | 已完成 | 2021-03-03 | 2021-03-03 | xxx | - | 通过 | 已完成 | |
- | 前置任务 | 需要业务方配置的一些业务关停等操作,根据实际编写 | 已完成 | 2021-03-05 | 2021-03-05 | yyy | xxx | 通过 | 已完成 | |
- | 数据迁移 | 迁移对象记录、迁移时间记录、迁移中问题点记录 | 已完成 | 2021-03-05 | 2021-03-05 | xxx | yyy | 通过 | 已完成 | |
- | 数据校验 | 常规count校验,业务校验 | 已完成 | 2021-03-05 | 2021-03-05 | xxx | yyy | 通过 | 已完成 | |
- | 业务割接 | - | 已完成 | 2021-03-05 | 2021-03-05 | xxx | yyy | 通过 | 已完成 | |
- | 回滚方案 | - | 已完成 | 2021-03-05 | 2021-03-05 | xxx | yyy | 通过 | 已完成 | |
迁移任务2 |
3.2 迁移方案优化
迁移方案的演练,是为了踩一些我们未能预料到的坑,也是为了后续更好的评估整个方案的可行性,让我们对整个迁移方案做出更好的优化,迁移方案的优化主要关注:
- 迁移中未考虑到的点
- 迁移步骤可行性
- 迁移时间窗口限制
四、迁移割接/业务验证/回滚
迁移割接采取怎样的方案很大程序上依赖我们迁移方案的选择。我们需要确认好以下几点:
- 具体的割接执行步骤
- 评估割接时间窗口
- 数据/业务验证方式
- 回滚方案
4.1 数据验证脚本
一般而言检查mysql数据一致性使用count计数进行校验。对于checksum,checksum默认会对表记录一行一行进行校验,且校验期间会对表加read lock,若数据库存在大表,请谨慎操作!!!!
#cat mysql_check.sh
#!/bin/sh
#****************************************************************#
# ScriptName: mysql_check.sh
# Author: Sansi
# Create Date: 2019-11-23 15:30
# Modify Author: Sansi
# Modify Date: 2019-12-17 11:19
# Function:
# Usage: bash mysql_check.sh db1 [db2] ... [dbN]
#***************************************************************#
#!/bin/bash
#source info
#source_user=transfer
s_user=${db_user}
s_host=172.16.x.xx
s_port=${db_port}
s_password=${db_password}
#target info
t_user=${db_user}
t_host=172.16.x.xx
t_port=${db_port}
t_password=${db_password}
#检查db下每张表的checksum值
Checksum(){
t_checksum=`mysql -u${t_user} -p${t_password} -h${t_host} -P${t_port} -e "checksum table ${table_name};" | grep -v Checksum | awk '{print $2}'`
s_checksum=`mysql -u${s_user} -p${s_password} -h${s_host} -P${s_port} -e "checksum table ${table_name};" | grep -v Checksum | awk '{print $2}'`
if [ ${t_checksum} -eq ${s_checksum} ]
then
echo -e "${table_name} \tchecksum is ok"
else
echo -e "${table_name} \tchecksum is not ok"
fi
}
#遍历需要校验的所有db,并检查每个db下每张表的count数
for db in $@
do
echo -e "Begin check Schema : $db ..."
#导出db下所有表的目录
mysql -u${s_user} -p${s_password} -h${s_host} -P${s_port} -e "select concat(table_schema,\".\",table_name) from information_schema.tables where table_schema='$db';" | grep ${db}>${db}.txt
for table_name in `cat ${db}.txt`
do
t_count=`mysql -u${t_user} -p${t_password} -h${t_host} -P${t_port} -e "select count(*) from ${table_name};" | grep -v count`
s_count=`mysql -u${s_user} -p${s_password} -h${s_host} -P${s_port} -e "select count(*) from ${table_name};" | grep -v count`
if [ ${t_count} -eq ${s_count} ]
then
echo -e "${table_name} \t's count is ok ..."
#Checksum
else
echo -e "${table_name} \tcount is different\tScource:${s_count}\tTarget:${t_count}."
fi
done
done