DQL mutation添加重复记录未触发@id唯一约束如何解决?
问题原因
- GraphQL Schema 中定义的
@id字段唯一约束,是Dgraph GraphQL API 层单独实现的校验逻辑,仅作用于通过 GraphQL 入口提交的请求,不会下沉到 DQL 底层自动生效。 - DQL Schema 里的
@upsert索引本身不具备强制唯一的能力,它的作用是配合 Upsert 操作保证并发写入时冲突检测的正确性,不会主动阻止重复数据写入。 - 你通过 dgo 直接提交 DQL mutation 时,既绕开了 GraphQL 层的校验逻辑,又使用了不带冲突检查的普通 mutation,因此会出现重复插入的情况。
解决方法
可根据业务场景选择以下两种方案:
方案1:使用 DQL Upsert Mutation 代替普通 Mutation
自行在 mutation 前添加查询逻辑,检查目标字段是否已存在对应值,仅当无重复时执行写入,参考逻辑如下:
// 构造 Upsert 请求 query := ` query checkDuplicate($name: string, $slug: string) { var(func: eq(Product.name, $name)) { nameCount as count(uid) } var(func: eq(Product.slug, $slug)) { slugCount as count(uid) } isDuplicate() { duplicate: math(max(val(nameCount), val(slugCount))) } } ` mutation := &api.Mutation{ SetNquads: []byte(` _:newProduct <Product.name> "your_product_name" . _:newProduct <Product.slug> "your_product_slug" . _:newProduct <Product.image> "your_image_url" . _:newProduct <Product.created_at> "2024-01-01T00:00:00Z" . _:newProduct <Product.updated_at> "2024-01-01T00:00:00Z" . _:newProduct <dgraph.type> "Product" . `), // 仅当无重复时执行写入 Cond: `@if(eq(val(duplicate), 0))`, } req := &api.Request{ Query: query, Vars: map[string]string{ "$name": "your_product_name", "$slug": "your_product_slug", }, Mutations: []*api.Mutation{mutation}, CommitNow: true, } // 执行请求后判断写入结果,若 Upsert 条件不满足会返回无修改结果,自行抛出唯一性冲突错误即可
该方案可以保证即使在高并发场景下,也不会出现重复数据,@upsert 索引会保障 Upsert 操作的原子性。
方案2:业务层前置校验
写入前先查询确认 name 和 slug 无重复,再执行写入操作。该方案适合并发量较低的场景,高并发场景下需要额外加分布式锁避免竞态条件导致的重复写入。
内容的提问来源于stack exchange,提问作者Panagiotis Koursaris
相关产品推荐
相关产品推荐

