Spark MongoDB连接器执行Upsert时修改不可变字段_id报错如何解决
问题根源
报错的核心是MongoDB的_id字段为系统默认不可修改字段,Spark读取MongoDB数据时会默认将原文档的_id字段加载到DataFrame中,执行Upsert操作时Mongo Spark连接器默认会将_id纳入更新字段,触发不可变字段校验。同时你当前配置的分片键与集合实际分片键不匹配,也会加剧写入异常。
你使用的分片集合配置如下:
{ "_id" : "db_1.target_collection", "lastmodEpoch" : ObjectId("6076a37e37c2cca5853da6df"), "lastmod" : ISODate("1970-02-19T17:02:47.301Z"), "dropped" : false, "key" : { "kfuin" : "hashed" }, "unique" : false, "uuid" : UUID("57c30bbe-af83-4410-a51f-c04f3c7522f4") }
解决步骤
- 第一步:处理DataFrame中的
_id字段- 若你不需要基于原
_id做匹配更新,直接在写入前删除_id列即可:df = df.drop("_id") - 若你需要基于
_id匹配文档做Upsert,需额外添加配置idFieldList指定_id为匹配键,不纳入更新字段:.option("idFieldList", "_id")
- 若你不需要基于原
- 第二步:修正分片键配置
你的集合实际分片键为哈希类型的kfuin,需要将写入配置中的shardKey调整为和实际一致:.option('shardKey', '{kfuin: "hashed"}') - 第三步:补充Upsert开关配置
要执行Upsert操作需要显式开启upsert参数,根据你的更新需求选择对应配置:- 仅部分更新匹配文档的字段、保留原文档其他字段:
replaceDocument保持false,添加.option("upsert", "true") - 全量替换匹配到的整份文档:
replaceDocument设为true,添加.option("upsert", "true")
- 仅部分更新匹配文档的字段、保留原文档其他字段:
修正后的完整写入代码示例
# 可选:不需要_id做匹配时先删除该列 df = df.drop("_id") df.write.format('com.mongodb.spark.sql') \ .option('collection', 'target_collection') \ .option('replaceDocument', 'false') \ .option('shardKey', '{kfuin: "hashed"}') \ .option('upsert', 'true') \ # 若保留_id做匹配则添加下面这行配置 # .option('idFieldList', '_id') \ .mode('append') \ .save()
内容的提问来源于stack exchange,提问作者fq zzzhao
相关产品推荐
相关产品推荐

