如何判断数据迁移任务可以停止
通常,在业务割接完成后,为了防止源数据库的操作继续同步到目标数据库,造成数据覆盖问题,您可选择结束迁移任务。结束之前您需要确认完成以下几点:
- 请您确认至少在业务低峰期有过一次完整的数据对比。
- 完成业务割接。
a. 先中断业务(如果业务负载非常轻,也可以尝试不中断业务)。
b. 在源数据库端执行如下语句(此处以MySQL为例),并观察在1-5分钟内若无任何新会话执行SQL ,则可认为业务已经完全停止。
show processlist;
上述语句查询到的进程列表中,包括DRS迁移实例的连接,您需要确认除DRS迁移实例的连接外无任何新会话执行SQL,即可认为业务已经完全停止。
c. 实时同步时延为0,并稳定保持一段时间;同时,您可以使用数据级对比功能,进行割接前的最后一次数据级对比,耗时可参考之前的对比记录。
n 如果时间允许,则选择全部对比。
n 如果时间不允许,则推荐对比活跃表,关键业务表,第二步对比多次存在差异的表等。
d. 确定系统割接时机,业务系统指向目标数据库,业务对外恢复使用。
- 结束迁移任务,该操作仅删除了迁移实例,迁移任务仍显示在任务列表中,您可以进行查看或删除。
MySQL迁移中Definer强制转化后如何维持原业务用户权限体系
Definer的使用主要应用在视图、存储过程、触发器、事件等对象里,Definer并不会限制对象被调用的权限,但会限制对象访问数据库的权限。本场景下,用户在MySQL迁移过程中选择了“所有Definer迁移到该用户下”,则源库用户体系下其他用户账号在完成用户迁移后,如果用户迁移和权限授权都执行成功,则无需授权便可继续使用原业务(使用DRS用户迁移功能可以实现用户、权限、密码迁移),否则如果想在原来的用户权限体系下延用原业务,则需要进行授权后才具有Definer相关数据库对象的访问使用权限,从而保证原业务正常。
本章节主要介绍如何通过数据库命令行对用户账号进行授权的方法。
步骤 1 确保新用户(Definer统一使用指定账号)具备足够的权限执行视图、存储过程等相关SQL。
步骤 2通过MySQL官方客户端或者其它工具登录目标数据库。
步骤 3 通过如下命令查看需要授权的用户user当前权限详情。
show grants for 'user'@'host';
步骤 4 为了保证原业务不报错,使用如下命令给用户user授予涉及的数据库对象缺失的操作权限。
grant select,insert,update,delete on db_name.* to 'user'@'host';
一般情况下,访问数据库的权限包括:SELECT、CREATE、DROP、DELETE、INSERT、UPDATE、INDEX、EVENT、CREATE VIEW、CREATE ROUTINE、TRIGGER、EXECUTE。您需要根据具体的数据库对象查看缺少哪些权限,再进行授权操作。
对于存储过程和函数,必须保证用户user对其有拥有EXECUTE权限,授权SQL命令如下:
grant execute on db_name.function_name to 'user'@'host';
步骤 5 使用授权后的用户账号访问目标库对象,无异常报错表示授权成功。需要注意:在java项目工程中调用存储过程、函数如果出现 Java.sql.SQLException: User does not have access to metadata required to determine stored procedure parameter types. If rights can not be granted, configure connection with "noAccessToProcedureBodies=true" to have driver generate parameters that represent INOUT strings irregardless of actual parametertypes,则需要单独执行用户user对mysql.proc库的授权:
grant select on mysql.proc to 'user'@'host';
MySQL存储过程迁移上云后遇到调用权限的问题,如何解决
MySQL存储过程迁移上云后,可能会因为权限问题导致调用存储过程或函数出错。
针对该情况,不同的Definer策略有不同的处理方法。本章节主要以user1为示例,介绍两种迁移Definer的策略下的处理方法。
策略一
在测试连接页面的目标库信息中填写数据库用户名user1,所有Definer迁移到该用户下选“是”。
这种策略下,源库所有存储过程和方法的Definer迁移到目标库后账号都会自动修改为user1,host改为% 。若在目标库上出现调用存储过程失败的情况,可执行如下操作:
步骤 1 使用uesr1账号登录到目标库RDS for MySQL实例。
步骤 2 如果需要使用其他账号调用存储过程,则该账号需要具有execute权限。
步骤 3 通过如下语句,使用user1授予其他账号执行存储过程的权限。
其中user表示需要调用存储过程的其他账号:
GRANT EXECUTE ON db.* TO user;
步骤 4 如果需要通过Java调用存储过程,则需要通过如下语句,使用user1授予其他账号查询mysql.proc表的权限。
授权语句可参考如下语句,user表示需要调用存储过程的账号:
GRANT SELECT ON mysql.proc TO 'user'@'%';
策略二
在测试连接页面的目标库信息中填写数据库用户名user1,所有Definer迁移到该用户下选“否”。
这种策略下,源库所有存储过程和方法的Definer迁移到目标库后账号和host保持不变,选择此选项,需要配合2.3.5.1** **迁移用户功能,将源数据库的用户全部迁移,这样才能保持源数据库的权限体系完全不变。
如果您未选择用户权限迁移或者用户权限迁移时存在不支持迁移的账号,建议选择[策略一]( " ")来处理。
如何确保业务数据库的全部业务已经停止
业务切换时可通过如下方法确保业务数据库的全部业务已经停止:
步骤 1 在源数据库端执行如下语句,查看当前是否还存在有业务连接。
show processlist;
图 查看是否存在业务连接
步骤 2 可选: ****如果源数据库有业务连接,则通过结果中Host列的值来查找对应的业务进程并将其停止。
步骤 3 在源库执行如下语句,查看binlog位置并记录该值(file列取值:position列取值 ),此处将该值记为ckpt1。
show master status;
图 查看binlog位置
步骤 4 等待30s以上,在源库执行如下语句,查看binlog位置并记录该值(file列取值:position列取值 ),此处将该值记为ckpt2。ckpt1=ckpt2时,表示源数据库业务已基本停写。
show master status;
使用定时启动任务失败,迁移日志提示can not get agency token
使用定时启动任务功能时,如果使用的是子账号,需要使用“账户委托”,否则任务启动失败,迁移日志报:can not get agency token。
解决方案
目前针对该情况,分别提供如下解决方案:
方法一:使用主账号重新创建任务,启动方式选择“定时启动”。
方法二:使用主账号在子账号所在的用户组添加Security Administrator权限后,重新创建任务,启动方式选择“定时启动”。
方法三:重新创建任务,启动方式选择“立即启动”。
RDS for MySQL不支持MyISAM引擎表,迁移时MyISAM如何处理
基于以下原因,RDS for MySQL目前不支持MyISAM引擎。
MyISAM引擎表不支持事务,仅支持表级别锁,导致读写操作相互冲突。
MyISAM对数据完整性的保护存在缺陷,且这些缺陷会导致数据库数据的损坏甚至丢失。
MyISAM在出现数据损害情况下,很多都需要手动修复,无法通过产品服务提供的恢复功能进行数据恢复。
MyISAM向InnoDB的迁移透明,大多数情况不需要改动建表的代码,云数据库自动转换InnoDB即可完成迁移。
DRS在迁移过程中,会自动将MyISAM转换为InnoDB。针对MyISAM引擎表不支持事务这一特点,为了确保MyISAM表的数据一致性, DRS会借助主键来实现最终数据的一致。如果需要迁移没有主键的MyISAM表,建议选择无业务期启动迁移任务,以确保数据的一致性。
低版本迁移至MySQL 8.0,应该注意哪些问题
MySQL 8.0较MySQL 5.7增加了一些新的特性,并在性能表现上存在差异。迁移前,需要做兼容性分析并给出解决方案。可以从兼容性、系统变量等方面考虑。
兼容性分析:
针对MySQL8.0社区版与MySQL5.7社区版进行分析,包括以下两方面:
a. 不影响迁移,但使用方法出现差异。
兼容性 | 检查项 | 作用 | 状态 | 解决方案 |
---|---|---|---|---|
数据类型或函数 | ENCODE()函数 | 加密 | 移除 | AES_ENCRYPT()函数代替 |
DECODE()函数 | 解密 | 移除 | AES_DECRYPT()函数代替 | |
ENCRYPT()函数 | 加密 | 移除 | SHA2()函数代替 | |
DES_ENCRYPT()函数 | 加密 | 移除 | AES_ENCRYPT()函数代替 | |
DES_DECRYPT()函数 | 解密 | 移除 | AES_DECRYPT()函数代替 | |
JSON_APPEND()函数 | 增加json元素 | 移除 | JSON_ARRAY_APPEND()函数代替 | |
PASSWORD()函数 | 修改用户密码 | 移除 | ALTER USER user IDENTIFIED BY 'auth_string'; | |
JSON_MERGE()函数 | 将多个json合并为一个 | 废弃 | JSON_MERGE_PERSERVE()函数代替 | |
SQL MODE | NO_AUTO_CREATE_USER、DB2, MAXDB, MSSQL, MYSQL323, MYSQL40, ORACLE, POSTGRESQL, NO_FIELD_OPTIONS, NO_KEY_OPTIONS, NO_TABLE_OPTIONS | - | 移除 | - |
外键约束长度 | 外键约束名称不能超过64个字符 | - | - | SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME IN (SELECT LEFT(SUBSTR(ID,INSTR(ID,'/')+1), INSTR(SUBSTR(ID,INSTR(ID,'/')+1),'ibfk')-1) FROM INFORMATION_SCHEMA.INNODB_SYS_FOREIGN WHERE LENGTH(SUBSTR(ID,INSTR(ID,'/')+1))>64);使用ALTER TABLE调整长度 |
features | GRANT创建用户 | - | 移除 | CREATE USER |
GRANT修改用户信息 | - | 移除 | ALTER USER | |
IDENTIFIED BY PASSWORD 'auth_string' | 设置密码 | 移除 | IDENTIFIED WITH auth_plugin AS 'auth_string' | |
SQL语句中的\N | NULL | 移除 | NULL代替 | |
PROCEDURE ANALYSE()语法 | 对MySQL字段值进行统计分析后给出建议的字段类型 | 移除 | - | |
空间函数 | - | - | - | |
mysql_install_db | 初始化 | 移除 | mysqld --initialize或--initialize-insecure |
b. 影响迁移,需要提前做检查。
兼容性 | 检查项 | 作用 | 状态 | 解决方案 | 原始用法 |
---|---|---|---|---|---|
保留关键字 | cume_dist、dense_rank、empty、except、first_value、grouping、groups、json_table、lag、last_value、lateral、lead、nth_value、ntile、of、over、percent_rank、rank、recursive、row_number、system、window | - | 新增 | SET sql_mode = 'ANSI_QUOTES' | 名称:数据库、表、索引、列、alias、view、存储过程、分区、表空间 |
字符集 | UTF8MB3 | - | 废弃 | 使用UTF8MB4代替 | - |
分区表 | 不得出现不支持本地分区的存储引擎的分区表 | - | 移除 | SELECT TABLE_SCHEMA, TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE ENGINE NOT IN ('innodb', 'ndbcluster') AND CREATE_OPTIONS LIKE '%partitioned%';可按照下述两种方式解决:(1)ALTER TABLE table_name ENGINE=INNODB;(2)ALTER TABLE table_name REMOVE PARTITIONING; | 不支持MyISAM |
语法 | group by … asc/desc | 升序/降序 | 移除 | 使用order by子句代替 | view、function等 |
名称长度 | view的列名称不能超过64个字符 | - | - | alter处理 | 最多255个字符 |
enum或set元素的总长度不能超过255个字符 | - | - | 用户处理 | 最大64K | |
大小写 | lower_case_table_names | MySQL设置字母大小写是否敏感 | - | 升级过程中,如果设置该参数为1,则必须确保schema和table名称必须是小写的SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME != LOWER(TABLE_NAME) AND TABLE_TYPE = 'BASE TABLE';SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME != LOWER(SCHEMA_NAME); | - |
触发器 | 是否有空定义或者无效的创建上下文 | - | - | show triggers查看,检测character_set_client、 collation_connection、Database Collation属性 | - |
系统变量默认值变更
针对社区版MySQL5.7与8.0版本的默认值作对比,默认值不影响迁移,但对迁移后的业务会产生影响。
序号 | parameter/option | community | 作用 | 备注 |
---|---|---|---|---|
原默认值 | 新默认值 | |||
Server | ||||
1 | character_set_server | latin1 | utf8mb4 | - |
2 | collation_server | latin1_swedish_ci | utf8mb4_0900_ai_ci | - |
3 | explicit_defaults_for_timestamp | OFF | ON | 更新某一行时是否更新timestamp列 |
4 | optimizer_trace_max_mem_size | 16KB | 1MB | - |
5 | validate_password_check_user_name | OFF | ON | - |
6 | back_log | -1 (autosize) changed from : back_log = 50 + (max_connections / 5) | -1 (autosize) changed to : back_log = max_connections | 在MySQL暂时停止回答新请求之前的短时间内多少个请求可以被存在堆栈中。 |
7 | max_allowed_packet | 4194304 (4MB) | 67108864 (64MB) | 限制Server接受的数据包大小 |
8 | max_error_count | 64 | 1024 | 控制显示告警的个数 |
9 | event_scheduler | OFF | ON | - |
10 | table_open_cache | 2000 | 4000 | - |
11 | log_error_verbosity | 3 (Notes) | 2 (Warning) | - |
INNODB | ||||
1 | innodb_undo_tablespaces | 0 | 2 | - |
2 | innodb_undo_log_truncate | OFF | ON | - |
3 | innodb_flush_method | NULL | fsync (Unix),unbuffered (Windows) | 控制innodb数据文件及redo log的打开、刷写模式 |
4 | innodb_autoinc_lock_mode | 1 (consecutive) | 2 (interleaved) | 控制着在向有auto_increment 列的表插入数据时,相关锁的行为; |
5 | innodb_flush_neighbors | 1 (enable) | 0 (disable) | 从缓冲池刷新页面是否也刷新相同范围内的其他脏页。 |
6 | innodb_max_dirty_pages_pct_lwm | 0 (%) | 10 (%) | 影响innodb刷新脏页行为 |
7 | innodb_max_dirty_pages_pct | 75 (%) | 90 (%) | 影响innodb刷新脏页行为 |
PERFORMANCE SCHEMA | 整体是不是开的 | - | - | - |
REPLICATION | ||||
1 | log_bin | OFF | ON | - |
2 | server_id | 0 | 1 | - |
3 | log-slave-updates | OFF | ON | - |
4 | expire_log_days | 0 | 30 | - |
5 | master-info-repository | FILE | TABLE | - |
6 | relay-log-info-repository | FILE | TABLE | - |
7 | transaction-write-set-extraction | OFF | XXHASH64 | - |
8 | slave_rows_search_algorithms | INDEX_SCAN, TABLE_SCAN | INDEX_SCAN, HASH_SCAN | - |
移除系统变量
针对社区版MySQL 5.7与8.0进行分析,移除系统变量不影响迁移。
移除变量 |
---|
innodb_locks_unsafe_for_binlog |
log_builtin_as_identified_by_password |
old_passwords |
query_cache_limit |
query_cache_min_res_unit |
query_cache_size |
query_cache_type |
query_cache_wlock_invalidate |
ndb_cache_check_time |
ignore_db_dirs |
tx_isolation |
tx_read_only |
sync_frm |
secure_auth |
multi_range_count |
log_error_verbosity |
sql_log_bin |
metadata_locks_cache_size |
metadata_locks_hash_instances |
date_format |
datetime_format |
time_format |
max_tmp_tables |
ignore_builtin_innodb |
innodb_support_xa |
innodb_undo_logs |
innodb_undo_tablespaces |
internal_tmp_disk_storage_engine |
MongoDB数据库迁移过程中,源数据库出现内存溢出(OOM)是什么原因
场景描述
在进行MongoDB数据库迁移的过程中,出现源数据库内存溢出(OOM),导致源数据库不可用,迁移失败。
问题分析
出现上述内存溢出可能存在如下原因:
源数据库的mongod服务单独部署在一台机器上,如果这种情况下在迁移过程中出现内存溢出,一般就是因为在迁移过程中源库在执行会大量消耗内存的操作,比如:创建索引,排序查询等。
源数据库的mongod服务和其他服务同时部署在一台机器上,而且没有设置cacheSizeGB的大小,这种情况下,如果因为其他服务消耗掉内存导致不能给wiredTiger引擎保证的内存,则会出现内存溢出的情况。
一般默认情况下,mongod的wiredTiger引擎可以使用整个机器内存减一的50%(3.2的版本)或者60%(3.4以后的版本)。
解决方案
如果mongod服务是单独部署在一台机器上,则在迁移过程中最好不要执行会大量消耗内存的操作,比如:创建索引,排序查询等。
如果mongod服务和其他服务共同部署在一台机器上,则建议给mongod的wiredTiger引擎加上cacheSizeGB的参数,设置的值为机器最小空闲内存的一半,保证所有服务在高峰期所使用的内存不会超过分配给wiredTiger引擎的内存。
如何关闭集合均衡器Balancer
使用DRS服务进行MongoDB数据库分片集群到分片集群的迁移,必须关闭要迁移集合的均衡器Balancer。
迁移结束后请开启Balancer,因为在迁移期间关闭了Balancer,源数据库的不同shard可能产生了不等量的块(chunk),在Balancer开启之后集群shard之间的块(chunk)移动会暂时影响源数据库的性能。
关闭Balancer的步骤
步骤 1通过Mongo Shell 登录数据库。
步骤 2 在mongos节点命令窗口中,使用如下命令,切换至config数据库。
use config
步骤 3 执行如下命令,判断是否可以关闭Balancer。
while( sh.isBalancerRunning() ) {
print("waiting...");
sleep(1000);** **
}
如果返回结果是waiting,则表示当前Balancer正在执行块(chunk)迁移,此时不能执行关闭Balancer的命令,否则可能引起数据不一致。
图 查看输出结果
如果返回结果是空,则表示当前Balancer没有在进行块(chunk)迁移,此时可以执行下一步的关闭Balancer的命令。
步骤 4 关闭Balancer。
如果是整个实例的迁移,则执行如下命令,可以关闭整个实例的Balancer。
sh.stopBalancer()
如果要关闭待迁移且已经开启了分片的集合的Balancer,则执行如下命令:
sh.disableBalancing("database.collection")
其中database.collection表示要关闭的集合的namespace。
如何批量导出、导入事件(event)和触发器(trigger)
在进行MySQL到MySQL的迁移时,若任务结束后发现迁移日志中提示迁移事件和触发器失败,可手动迁移。
本小节主要介绍批量导出导入事件和触发器的具体操作。
步骤 5 从源库批量导出触发器。
- 在源库执行以下语句,获取TRIGGER_SCHEMA和TRIGGER_NAME。
SELECT TRIGGER_SCHEMA,TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA in ('DB1','DB2','DB3') order by TRIGGER_NAME;
上述语句中,DB1,DB2,DB3分别表示从源库待迁移到目标库的数据库。
- 在源库执行如下语句,从字段SQL Original Statement中获取源库创建触发器的语句。
SHOW CREATE TRIGGER TRIGGER_SCHEMA.TRIGGER_NAME \G;
上述语句中,TRIGGER_SCHEMA.TRIGGER_NAME填写的为[步骤 1.1 ]( " ")中查询到的TRIGGER_SCHEMA和TRIGGER_NAME具体值。
步骤 6 从源库批量导出事件。
- 在源库执行以下语句,获取EVENT_SCHEMA和EVENT_NAME。
SELECT EVENT_SCHEMA,EVENT_NAME FROM INFORMATION_SCHEMA.EVENTS WHERE EVENT_SCHEMA in ('DB1','DB2','DB3') order by EVENT_NAME;
上述语句中,DB1,DB2,DB3分别表示从源库待迁移到目标库的数据库。
- 在源库执行如下语句,从字段SQL Original Statement中获取源库创建事件的语句。
SHOW CREATE EVENT EVENT_SCHEMA.EVENT_NAME \G;
上述语句中,EVENT_SCHEMA.EVENT_NAME填写的为[步骤 2.1 ]( " ")中查询到的EVENT_SCHEMA和EVENT_NAME具体值。
步骤 7 导入触发器和事件。
在目标库重新执行从源库导出的创建触发器和创建事件语句。
源库参数lower_case_table_names=1时,为什么不允许迁移包含大写字母的库或者表
场景描述
当源库参数lower_case_table_names=1时,无法迁移包含大写字母的库或者表。
问题分析
当源库的lower_case_table_names 参数值为1时,MySQL会将库名或者表名转换成小写再进行查找。若存在以大写字母形式创建的库或者表,那么在lower_case_table_names参数值为1的情况下,MySQL将无法找到这个库或表,报告查询失败。也就是说,若lower_case_table_names的参数值为1时,大写字母的库或表很可能是不可访问的。
解决方案
目前针对该情况,分别提供如下解决方案:
方法一
修改源库lower_case_table_names的参数值为0 (即大小写敏感),并且保证源库与目标库的该参数值一致。
方法二
若无法永久修改lower_case_table_names,可临时将源库lower_case_table_names修改为0,然后执行如下操作。
对于表,可以使用如下语句将表名转换为小写:
alter table BigTab
rename to bigtab
对于库,则需要导出后,修改库名为小写,再进行导入。
修改库名或表名之后,需要维护权限的一致性,以免影响应用访问。
方法三
对象选择时不迁移该库或者该表。
分片集群MongoDB迁移前清除孤儿文档
什么是孤儿文档
MongoDB负载均衡器(Balancer)会根据集合的分片键(Shard key)均衡数据。Balancer的工作原理是:需要Balancer的数据块(Chunk)先复制到目标Shard,成功后再删除原Shard上的Chunk,来完成一次Chunk迁移,通过多次Chunk迁移来实现均衡。在Chunk迁移时,如果发生网络闪断等不可预知的场景,完成了复制但没有完成删除,那么对同一条文档会同时存在于两个Shard上。因为Chunk迁移在MongoDB上是感知的,config会更新这条文档应该在哪个Shard上,那么另一个Shard上的文档会存在但不会被感知,后续的update、delete操作都不会作用于这个错误的Shard上的文档,那么这条文档被称为孤儿文档(Orphaned Document)。
迁移影响
DRS在迁移集群时,会从Shard上抽取全量数据。正常文档和孤儿文档在不同的Shard上,DRS不会感知,都会迁移到目标库。DRS针对MongoDB迁移的冲突策略为忽略,因此最终目标库上的文档取决于哪个文档先被迁过去,会造成数据内容或行数不一致。
操作步骤
步骤 1 联系技术支持,获取用于清除孤儿文档的cleanupOrphaned脚本文件,然后解压。
步骤 2 修改cleanupOrphaned.js脚本文件,将test替换为待清理孤儿文档的数据库名。
步骤 3 执行以下命令,清理Shard节点下指定的数据库中所有集合的孤儿文档。
mongo --host ShardIP ** --port** Primaryport --authenticationDatabase database -u username -p passowrd cleanupOrphaned.js
ShardIP:Shard节点的IP地址。
Primaryport:Shard节点中的Primary节点的服务端口。
database:鉴权数据库名,即数据库账号所属的数据库。
username:登录数据库的账号。
passowrd:登录数据库的密码。
如果您有多个数据库,您需要重复执行步骤[步骤 2 ]( " ")和步骤[步骤 3 ]( " "),分别为每个数据库的每个Shard节点清理孤立文档。