1. querySql:复杂查询的终极解决方案
第一次遇到需要同步多表JOIN结果时,我对着DataX的table和where配置发呆了半小时。直到发现querySql这个神器——原来它才是处理复杂查询的"瑞士军刀"。不同于基础的table+column配置,querySql允许你直接编写完整SQL语句,就像在数据库客户端里自由操作一样。
最典型的场景就是跨表关联查询。比如电商系统中,订单表orders需要与用户表users关联,同步包含用户名的订单数据。传统方式可能需要先同步两张表再处理,而用querySql只需一句:
SELECT o.order_id, o.amount, u.username FROM orders o JOIN users u ON o.user_id=u.user_id WHERE o.create_time>'2023-01-01'注意这三个坑:
- 与基础配置互斥:一旦启用querySql,table/column/where/splitPk配置都会失效(控制台会有警告日志)
- 字段映射问题:writer端的column配置仍需与查询结果列一一对应
- 性能隐患:复杂JOIN可能造成源库压力,建议在业务低峰期执行
实测一个千万级表关联查询,合理使用querySql能使同步任务配置行数减少70%。但要注意,不是所有Reader插件都支持,目前仅适用于RDBMS系列(MySQL/Oracle等)和HBase等部分插件。
2. preSql与postSql:数据清洗的双子星
去年做数据迁移时遇到个头疼问题:需要先清空目标表再导入,且要对导入数据做统计校验。手动执行?太low;写脚本?太麻烦。直到发现preSql/postSql这对组合拳,我才明白什么是"配置即脚本"。
2.1 preSql的魔法时刻
preSql会在数据写入前执行,常见场景包括:
- 清空目标表(
TRUNCATE table@) - 创建临时表(
CREATE TABLE temp_@ LIKE @table) - 添加约束(
ALTER TABLE @table ADD INDEX idx_name(name))
特别注意这个@table占位符,它能自动替换为实际表名。比如配置分表同步时:
"preSql": ["DELETE FROM @table WHERE create_date='${bizdate}'"]会针对每个分表执行对应的DELETE操作。我在金融项目中就靠这个特性,实现了按业务日期清理历史数据再全量更新的需求。
2.2 postSql的收尾艺术
数据写入完成后,postSql才开始它的表演。典型用法:
- 刷新物化视图(
REFRESH MATERIALIZED VIEW mv_order) - 记录同步日志(
INSERT INTO sync_log VALUES(...)) - 数据质量检查(
CALL check_data_quality('@table'))
踩过的一个坑:postSql执行失败不会回滚已写入数据!有次因为权限问题导致统计SQL执行失败,但数据已经入库。后来我改成在preSql里开启事务,postSql里提交,完美解决:
"preSql": ["BEGIN"], "postSql": ["COMMIT"]3. splitPk:并发加速的秘密武器
同步5000万用户数据时,单线程跑了3小时。加上"splitPk": "user_id"后,20分钟搞定——这就是分片键的威力。splitPk的原理是把数据按主键范围拆分成多个分片,并行读取。
最佳实践指南:
- 选择区分度高的列:优先用自增主键,避免用性别等低区分度字段
- 数值类型最优:只支持整型(INT/BIGINT),用字符串会报错
- 分片数控制:通过
channel参数配合设置,建议每个分片500万条左右
{ "job": { "setting": { "speed": { "channel": 8 } }, "content": [{ "reader": { "parameter": { "splitPk": "id", // 其他配置... } } }] } }遇到过的一个深坑:用UUID做主键的表设置splitPk后性能反而下降。后来发现是因为UUID无序导致数据倾斜,最终改用范围分片方案解决。
4. 组合使用的高阶玩法
这些配置项单独使用已经很强,但组合起来更能解决复杂场景。分享两个实战案例:
案例1:跨库数据清洗
{ "querySql": "SELECT raw_data FROM source_db.table_a WHERE status=1", "preSql": ["TRUNCATE temp_table"], "postSql": [ "CALL transform_procedure()", "INSERT INTO log_table VALUES('sync_done')" ] }案例2:分库分表聚合
{ "splitPk": "order_id", "querySql": "SELECT * FROM orders WHERE mod(order_id,10)=${分片序号}", "preSql": ["ALTER TABLE target_table DISABLE KEYS"], "postSql": [ "UPDATE stats SET count=(SELECT COUNT(*) FROM target_table)", "ALTER TABLE target_table ENABLE KEYS" ] }特别注意配置优先级:当querySql存在时,splitPk配置会失效。有次排查半天性能问题,就是因为同时配置了querySql和splitPk导致并发失效。正确的做法是在querySql里手动实现分片逻辑,比如用WHERE id BETWEEN x AND y。