Apache SeaTunnel MongoDB到Doris同步时,有哪些常见问题及解决方案?
摘要:最近在几个数据中台项目里,频繁用SeaTunnel做MongoDB到Doris的数据同步。说实话,这活儿看着简单,真上手了才发现坑不少。尤其是生产环境,数据量大、结构复杂,稍不注意就掉坑里。
最近在几个数据中台项目里,频繁用SeaTunnel做MongoDB到Doris的数据同步。说实话,这活儿看着简单,真上手了才发现坑不少。尤其是生产环境,数据量大、结构复杂,稍不注意就掉坑里。
这篇文章不打算重复那些基础配置步骤——网上已经有很多了。我想聚焦在实际生产环境中,那些最容易让人栽跟头的地方。特别是当你面对的是TB级别的MongoDB集合,需要稳定同步到Doris做实时分析时,下面这五个坑点,几乎每个都会遇到。我会结合具体的报错日志、排查思路,以及我们团队摸索出来的解决方案,帮你把这些坑一个个填平。
1. 数据类型映射:BSON到SQL的转换问题
MongoDB的BSON类型系统和Doris的SQL类型系统,表面上看起来能自动映射,实际上藏着不少“惊喜”。最典型的就是Decimal128和ObjectId的处理。
1.1 Decimal128的精度丢失问题
MongoDB里用Decimal128存储高精度数值,比如金融交易的金额。SeaTunnel默认会把它映射成Doris的DECIMAL类型,但这里有个关键限制:Doris的DECIMAL最大支持38位精度,而Decimal128是34位小数位。如果你在SeaTunnel的schema里没明确指定精度,很可能遇到这样的错误:
java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal result
解决方案是在schema里显式声明精度。别用自动推断,手动控制:
source {
MongoDB {
uri = "mongodb://user:password@host:27017"
database = "finance"
collection = "transactions"
schema = {
fields {
_id = string
amount = "decimal(38, 18)" # 明确指定38位总精度,18位小数位
currency = string
timestamp = timestamp
}
}
}
}
注意:如果你的数据里Decimal128的小数位超过18位,需要根据实际情况调整。我们有个电商项目,优惠券计算精度要求高,就用了decimal(38, 24)。
1.2 ObjectId和嵌套文档的序列化坑
MongoDB的_id字段默认是ObjectId类型,SeaTunnel会把它转成字符串。这看起来没问题,直到你发现Doris表里的主键冲突——因为ObjectId转字符串后,Doris的UNIQUE KEY检查可能会出问题。
更麻烦的是嵌套文档。MongoDB里很常见的结构:
{
"_id": ObjectId("507f1f77bcf86cd799439011"),
"user": {
"name": "张三",
"address": {
"city": "北京",
"district": "朝阳区"
}
}
}
SeaTunnel默认会把整个user对象转成一个JSON字符串存到Doris的一个VARCHAR字段里。如果你想在Doris里直接查询user.address.city,就得用JSON函数解析,性能很差。
我们的做法是在SeaTunnel里用transform插件提前展开:
transform {
# 展开嵌套字段
sql {
query = """
SELECT
_id,
user.name as user_name,
user.address.city as city,
user.address.district as district
FROM mongodb_source
"""
}
}
sink {
Doris {
fenodes = "fe1:8030,fe2:8030"
username = "admin"
password = "***"
database = "analytics"
table = "user_flat"
# 现在表结构是平的,查询效率高
}
}
如果嵌套层级太深或者不确定,也可以考虑在Doris里用MAP类型,但要注意2.0以上版本才支持。
