Répertoire d'articles
- 1. Recherche environnementale
- 2. Formulation du plan
- 3. Exercice de migration
- 4. Migration et basculement / vérification commerciale / restauration
Lorsque nous sommes confrontés à un système d'entreprise de base de données inconnu et que nous devons terminer la migration de l'ensemble de l'entreprise (application + base de données) dans un certain laps de temps, recherche environnementale préliminaire, formulation du plan de migration, exercice de test de migration, basculement de la migration formelle, retour en arrière de la migration plan, chaque étape est une priorité absolue et indispensable.Ce document résout principalement certaines préoccupations générales pendant tout le processus de migration, ce qui peut vous aider à démarrer rapidement la migration de la base de données.
1. Recherche environnementale
Quelle que soit la base de données que nous migrons, la première étape que nous devons faire est de comprendre les informations environnementales de base de la base de données que nous voulons migrer, son utilisation et le flux de l'ensemble de la topologie métier. De cette façon, nous pouvons essayer d'éviter certains détours lors du premier test de migration.
1.1 Informations sur le serveur
Système d'entreprise possédé | surnom du serveur | IP du serveur | configuration du serveur | Configuration du stockage | IP de la base de données: port | Version de la base de données |
---|---|---|---|---|---|---|
xxx_production | xxx_prod | 172.16.xx.xx | 16C128G | SSD 2T | 172.16.xx.xx: 3306 | MySQL 5.7.33 |
xxx_ pré-version | xxx_stg |
1.2 Informations sur la base de données
1.2.1 Informations de base sur la base de données
Si MySQL est migré vers un environnement auto-construit, nous ferions mieux d'utiliser le fichier my.cnf de l'environnement source pour initialiser et appliquer la base de données, mais si nous devons migrer la base de données auto-construite vers un environnement de base de données cloud, certains facteurs clés qui affectent l'utilisation de la base de données Les paramètres affecteront la formulation de notre plan de migration et l'utilisation commerciale normale de l'environnement cible. Nous devons encore nous concentrer sur les paramètres suivants:
Version de la base de données | Niveau d'isolement des transactions | Soumission automatique | Server_id | Ouvrir ou non binlog | mode binlog | mode miroir binlog | Sensible aux majuscules et minuscules | Limite supérieure du préfixe d'index | sql_mode |
---|---|---|---|---|---|---|---|---|---|
5.7.20-log | LIRE-ENGAGÉ | 1 | 33061 | 1 | LIGNE | PLEIN | 0 | 1 | STRICT_TRANS_TABLES, NO_ZERO_IN_DATE |
Prise en charge SQL associée:
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 Informations sur l'utilisateur de la base de données
Compte de base de données | Autorité | Affaires |
---|---|---|
surveiller@% | PROCESSUS DE SUBVENTION, CLIENT DE RÉPLICATION ACTIVÉ . Surveiller'@'%' | xxx_a |
root @ localhost | ACCORDEZ TOUS LES PRIVILÈGES . TO 'root' @ 'localhost' AVEC L'OPTION GRANT | xxx_b |
sansi_rw @% | ACCORDEZ TOUS LES PRIVILÈGES . À 'sansi_rw' @ '%' | xxx_c |
Prise en charge SQL associée:
-- 查看当前数据库用户列表
mysql> select user,host from mysql.user;
-- 查看制定数据库用户权限
mysql> show grants for 'root'@'localhost';
1.3 Recherche d'informations sur les tables de la bibliothèque
1.3.1 Vue d'ensemble des statistiques des objets de base de données
base de données | Type d'objet | Nombre d'objets |
---|---|---|
db1 | ÉVÉNEMENTS | 1 |
db1 | PROCÉDURE | 2 |
db1 | TABLE | 4 |
db1 | DÉCLENCHEUR | 1 |
db1 | VUE | 2 |
1.3.2 Vue d'ensemble des statistiques des informations de la base de données
base de données | jeu de caractères | Règles de relecture | Nombre estimé d'enregistrements | Capacité de données estimée (Mo) | Capacité d'index estimée (Mo) | Espace libre estimé (Mo) | Taille totale estimée (Mo) |
---|---|---|---|---|---|---|---|
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 | NUL | 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 | dix | 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 | NUL | NUL | NUL | NUL | NUL |
1.3.3 Aperçu des statistiques des informations de table
Nom de la base de données | Nom de la table | moteur | Mode ligne | Y a-t-il une clé primaire | Capacité de données estimée (Mo) | Capacité d'index estimée (Mo) | Espace libre estimé (Mo) | Taille totale estimée (Mo) | Nombre estimé de lignes |
---|---|---|---|---|---|---|---|---|---|
db1 | t2 | InnoDB | Dynamique | non | 0,01 | 0,00 | 0,00 | 0,01 | 8 |
db1 | AAA_bbb | InnoDB | Dynamique | non | 0,01 | 0,00 | 0,00 | 0,01 | 2 |
db1 | t1 | InnoDB | Dynamique | Avoir | 0,18 | 0,00 | 0,00 | 0,18 | 2605 |
db1 | enfant | InnoDB | Dynamique | Avoir | 0,01 | 0,00 | 0,00 | 0,01 | 0 |
db1 | parent | InnoDB | Dynamique | Avoir | 0,01 | 0,00 | 0,00 | 0,01 | 0 |
1.3.4 S'il existe une table de noms en double après avoir ignoré le respect de la casse
Les données | Nom de la table de la bibliothèque | Statistiques |
---|---|---|
xx | xx | 2 |
Prise en charge SQL associée:
-- 数据库对象统计概览
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 Topologie du flux de données
数据流拓扑可帮助我们了解在整个业务模块中,该数据库的整个数据来源以及流向。首先就拿数据源来讲,可能数据库的数据源除应用程序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