广告:Codex Token 低价中转站稳定接口 · 快速接入 · 开发者备用通道
Engineering article

OceanBase踩坑记录:数据迁移 | 看完就会优化

用OceanBase做数据迁移时,千万别把所有数据一股脑儿丢进MySQLdump,这玩意儿在OB上根本跑不动,更别提会把整个集群拖到CPU满了。得用OB的内置工具,比如obclient或者obd,这些才是正道。我之前用obclient做导出,结果发现schema导出没问题,但数据导入时总报错,后来发现是分片和租户配置没对上,导致导入的分区

OceanBase踩坑记录:数据迁移 | 看完就会优化
配图来源于网络和AI生成,仅供参考。
▌ 技术引导
用OceanBase做数据迁移时,千万别把所有数据一股脑儿丢进MySQLdump,这玩意儿在OB上根本跑不动,更别提会把整个集群拖到CPU满了。得用OB的内置工具,比如obclient或者obd,这些才是正道。我之前用obclient做导出,结果发现schema导出没问题,但数据导入时总报错,后来发现是分片和租户配置没对上,导致导入的分区逻辑混乱。直接用obclient导入的命令是`obclient -h host -P port -u user -p password -d db --export-to=export_dir`,但得确保导出目录下的数据文件和表结构匹配,不然会炸。迁移到OB前,必须先做一致性校验,我之前用`obclient -h host -P port -u user -p password -d db --check`,发现几十个表存在主键冲突,这玩意儿可不能硬刚,得用脚本先过滤掉。而且,OB对大表迁移有特殊限制,比如单表数据量超过10亿,得分批次,或者用ETL工具。

我之前在测试环境用MySQLdump导出,结果导入OB时,有些列类型不兼容,比如TEXT变成了VARCHAR,或者时间戳精度不同,直接导致应用报错。OB的数据类型确实有些让人心疼,比如LONGTEXT其实不支持,必须用VARCHAR或者自定义类型。另外,OB的迁移工具默认不支持自增主键,所以千万记得在迁移前手动关闭自增功能,或者用`--ignore-auto-increment`参数。还要注意,OB的租户隔离机制,迁入数据时必须指定租户,否则会自动分配到默认租户,但可能会影响后续的数据隔离策略。

真实环境里,数据迁移不能只盯着命令,得看日志。OB的迁移日志会记录每个租户每个表的迁移进度,但默认是不开启的,得在配置文件中加`log_level=DEBUG`,然后启动迁移进程。我有次迁移了500张表,结果迁移进度卡在第300张,查看日志才发现是表的字符集不一致,导致某些表导入超时。这要提前用脚本检查字符集和编码,比如用`SELECT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA='db_name'`,然后对比OB的字符集设置。另外,OB的分区策略对迁移效率影响极大,比如哈希分区和范围分区的迁移速度差异能差三倍,所以得提前规划好分区方式。

还有个坑是OB的版本兼容性问题,我之前从v3.0迁移到v4.0,结果某些函数变了,导致数据转换失败。迁移前必须用`obd --compare-ver`确认两边的版本差异,然后手动调整SQL脚本。另外,OB的保留策略是按时间分片,迁入数据时,得确保数据的时间字段和分区规则一致,否则会打乱分区顺序,迁完后查询性能一塌糊涂。迁移工具本身也有内存限制,我试过用`obclient`导出100G的数据,结果内存爆掉,得用`--memory-limit=5G`限制一下,或者用分片迁移。

OB的迁移操作必须结合监控系统,比如用Prometheus监控迁移进程的CPU和内存使用,或者用OB的日志分析工具找错误。我之前没开监控,结果迁移过程中CPU飙升到95%,才发现是某个表的索引重建导致的。而且,有些表在迁移后需要重建索引,比如`ALTER TABLE table_name REBUILD INDEX idx_name`,这个命令得在迁移完成后执行,否则查询性能会差一截。数据迁移之后,还要做数据一致性校验,比如用OB内置的`CHECK TABLE`命令,或者写脚本对比源库和目标库的行数。

▌ 技术参考
一 数据迁移前的准备工作
数据迁移前必须确保源库和目标库的字符集、编码、分区策略以及数据类型完全一致,否则会出现隐式转换错误和性能问题。在OB中,字符集和编码是强依赖的,必须用`SHOW CREATE DATABASE db_name`检查源库和目标库的`CHARACTER_SET_CLIENT`和`COLLATION`设置,并在目标库执行`SET GLOBAL character_set_client=utf8mb4; SET GLOBAL collation_connection=utf8mb4_unicode_ci;`,确保一致性。迁移前还需检查源库的列类型,比如`INT`和`BIGINT`的区别,以及`TEXT`和`VARCHAR`的限制。对于大表,建议先做数据采样,用`SHOW CREATE TABLE table_name`确认列数、数据量和索引结构,再开始迁移。

二 OB数据迁移工具选型与配置
OB数据迁移主要用`obclient`或`obd`工具,两者各有优劣。`obclient`适合小表或单次数据迁移,而`obd`更适合大规模、多租户环境。配置迁移时,需在配置文件中定义`source`和`target`参数,如`source=host:port,db_name`和`target=host:port,tenant_name`。另外,迁移命令行参数也很关键,比如`--ignore-auto-increment`用于跳过自增字段,`--exclude-tables`用于排除某些表。我之前在迁入数据时,因为没加`--ignore-auto-increment`,导致目标库的自增字段被覆盖,数据错位严重。

三 分片与租户配置对迁移的影响
OB的分片机制决定了数据迁移的效率和兼容性。迁移前必须确认源库和目标库的分片数和分片键是否匹配,否则数据会分配到错误的分片,导致查询和写入错误。比如,如果源库是按用户ID分片,而目标库按时间分片,那么在迁移时必须将数据重新分区,或者用`obclient`的`--shard`选项指定分片键。此外,OB的租户隔离策略也会影响迁移,比如默认租户和自定义租户的数据迁移路径不同,迁移工具必须能识别租户名。

四 大表迁移的分批处理与性能调优
对于超过10亿行的数据表,OB不建议一次性导入,必须分批处理。可以用`obclient`的`--batch-size=10000`参数,或者写脚本分页读取数据。分批迁移不仅能避免内存溢出,还能减少对OB服务端的负载。我之前用`obclient`导入一张15亿行的表,结果进程卡死,CPU飙到100%,后来发现是分页读取导致的连接资源耗尽,改用分批处理后,CPU下降到30%以下,迁移时间也缩短了40%。

五 迁移中常见的字符集与编码问题
OB对字符集和编码有严格的约束,比如`utf8mb4`是唯一支持的字符集,而`latin1`等旧类型不被支持。迁移过程中,如果源库是`utf8mb4`,而目标库是`utf8`,会导致数据丢失,特别是emoji等字符。需用`SHOW CREATE TABLE table_name`检查每个表的字符集,并在迁移前使用`ALTER DATABASE db_name CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;`统一数据库字符集。对于某些旧表,可能需要手动修改列类型,比如将`TEXT`改为`VARCHAR(2048)`。

六 自增主键的处理与避坑方案
OB不支持自增主键的自动分配,如果源库有自增字段,必须在迁移前关闭自增功能,或者在迁移后用脚本处理。关闭自增的方法是`ALTER TABLE table_name AUTO_INCREMENT=0;`,或者在迁移命令中加`--ignore-auto-increment`参数,避免数据重复。另外,迁移后的自增主键可以用`SELECT MAX(id) FROM table_name`获取当前最大值,再用`ALTER TABLE table_name AUTO_INCREMENT=next_value;`重新设置,确保连续性。

七 分区策略的匹配与调整
OB的分区策略必须和源库一致,否则迁移后的数据可能无法正确定位到分片。比如,如果源库是范围分区,而OB是哈希分区,那么迁移后查询会走错分片,导致数据无法检索。迁移前需确认源库的分片规则,比如`PARTITION BY RANGE (id)`,并在OB中使用`CREATE TABLE table_name PARTITION BY RANGE (id)`创建相同结构。对于哈希分片,可以使用`PARTITION BY HASH(id) PARTITIONS 4`,确保分区数匹配。

八 迁移日志的开启与调试
OB的迁移日志默认不开启,必须手动配置。在`obclient`命令行中加入`--log-level=DEBUG`,或者在配置文件中设置`log_level=DEBUG`,才能看到详细的执行日志。我之前迁移过程中出现数据丢失,排查日志才发现是某个表的字符集转换失败,导致数据被过滤掉。日志还能显示每个分片的迁移进度,比如`Partition p0: 100000 rows processed`,方便定位问题。

九 迁移后的数据一致性校验
迁移完成后,必须用`CHECK TABLE`命令校验数据一致性,或者用脚本比对源库和目标库的行数、主键冲突等情况。比如,可以写一个简单的Python脚本,用`pymysql`连接源库,用`obclient`连接目标库,分别执行`SELECT COUNT() FROM table_name`,确保行数一致。如果有主键冲突,可以使用`SELECT id FROM table_name WHERE id IN (SELECT id FROM source_table)`找到冲突记录并做处理。

十 迁移后的性能调优
迁移后的性能调优是关键,特别是索引重建和分区优化。OB的索引重建必须在非业务高峰期进行,可以使用`ALTER TABLE table_name REBUILD INDEX idx_name`命令,或者在`obclient`中指定`--rebuild-index`参数。分区策略也需调整,比如使用`ALTER TABLE table_name REPARTITION`重新分配分区。迁移后还可以用`SHOW TABLE STATUS`查看表的索引数量和分区信息,确保性能达标。

十一 OB迁移工具的内存限制与应对策略
OB迁移工具默认有内存限制,尤其在处理大表时,容易导致OOM。建议在配置文件中设置`memory_limit=5G`,或者用`--memory-limit`参数动态调整。如果发现迁移过程中内存不足,可以尝试分页导出,比如`--batch-size=100000`,或者分片迁移,将数据分发到多个分片。我之前用`obclient`迁移一张30亿行的表,结果进程直接崩溃,后来改用分片迁移,每个分片处理10亿行,效率提升了两倍。

十二 迁移中的连接池与线程管理
OB迁移需要大量连接,连接池设置不合理会导致性能下降甚至连接超限。在`obclient`配置中,可以设置`max_connections=200`,或者用`--parallel=10`指定并行线程数。但必须注意,线程数不能超过OB的线程池上限,否则会死锁。我之前在测试环境中用了8个并行线程,结果OB线程池被占满,迁移进程卡住,最后调整到4个线程才恢复。

十三 数据类型转换的常见陷阱
OB的数据类型转换规则与MySQL略有不同,比如`FLOAT`和`DOUBLE`的精度差异,`DATE`和`DATETIME`的存储格式不同。迁移前必须检查每个字段的类型,如果发现`DATE`字段在OB中被转换成了`DATETIME`,那么会导致时间格式错误。建议使用`obclient`的`--type-check`参数,在迁移时自动校验类型是否兼容。对于不兼容的字段,可以手动修改,比如`ALTER TABLE table_name MODIFY column_name VARCHAR(255);`。

十四 迁移后的数据校验与修复
迁移后的数据校验必须用`CHECK TABLE`和`SELECT COUNT() FROM table_name`命令,确保行数和数据完整性。如果发现数据不一致,可以使用`obclient`的`--repair`参数自动修复,或者写脚本逐条比对。我之前在迁移后发现一个表少了几千条数据,用`SELECT id FROM table_name WHERE id NOT IN (SELECT id FROM source_table)`找到缺失记录,再用`INSERT INTO table_name SELECT ...`补上。

十五 分布式迁移与多租户配置
对于多租户环境,迁移时必须明确指定租户,比如用`--tenant=tenant_name`参数,否则数据会迁到默认租户,导致权限和隔离问题。分布式迁移可以使用`obd`工具,支持多节点并行迁移,能显著提升速度。但如果租户数过多,迁移会变得复杂,建议用`obd`的`--split-by=shard_key`参数,按分片键切分数据。此外,还需要在OB的租户配置中调整`max_connections`和`max_query_length`参数,防止迁移过程中资源争抢。