MongoDB Go驱动使用UUID替代ObjectID:主键校验与错误识别
关于MongoDB中使用UUID作为_id的唯一性校验与重复错误识别
是否需要校验UUID的唯一性?
首先直接给结论:几乎完全不需要做循环校验。
你用uuid.New().String()生成的是UUID v4,这种类型的碰撞概率低到离谱——按照统计数据,每秒生成10亿个UUID,大概要100年才会有50%的概率出现一次碰撞。在常规业务场景里,这种概率低到可以直接忽略,完全没必要提前做循环重试,反而会增加不必要的数据库请求开销。
而且MongoDB默认会给_id字段创建唯一索引,就算极端到真的发生了碰撞,InsertOne也会直接返回错误,你只需要捕获这个错误做处理就行。
另外提个小细节:你的第一个CreateBook函数里,已经手动给book.ID赋值了UUID,之后又把insertResult.InsertedID赋值给book.ID,这一步其实是多余的——当你传入带有_id的文档时,MongoDB会直接使用你指定的值,InsertedID就是你设置的book.ID,不需要再重新赋值。
如何识别InsertOne返回的重复主键错误?
如果你确实需要处理极端情况下的重复键错误(或者只是想搞清楚怎么判断这种错误类型),可以通过Go的MongoDB驱动提供的错误类型来精准识别:
MongoDB的重复键错误对应的错误码是11000,在Go驱动中这类错误会被包装成mongo.WriteException类型。你可以通过类型断言和遍历错误列表来判断:
import ( "fmt" "go.mongodb.org/mongo-driver/mongo" ) func (mongoDB *MongoDB) CreateBook(ctx context.Context, book *ds.Book) error { book.ID = uuid.New().String() collection := mongoDB.Database.Collection(BookCollection) _, err := collection.InsertOne(ctx, book) // 注意用传入的ctx,别随便用context.TODO() if err != nil { // 判断是否为重复键错误 if writeErr, ok := err.(mongo.WriteException); ok { for _, we := range writeErr.WriteErrors { if we.Code == 11000 { // 这里就是重复主键错误,可自定义处理逻辑 return fmt.Errorf("book ID已存在: %w", err) } } } // 其他类型错误直接返回 return err } return nil }
顺便纠正你第二个循环版本里的小bug:
- 当
err == nil时,你写了return err,这会返回nil吗?不对,应该是return nil - 里面的
task.ID = uuid明显是笔误,应该是book.ID = uuid
总结
- 正常业务场景下,完全不需要循环校验UUID唯一性,UUIDv4的碰撞概率可以忽略不计
- 若要处理重复键错误,通过判断
mongo.WriteException中的错误码11000即可 - 代码里要保持上下文传递的一致性,尽量使用传入的
ctx而非context.TODO()
内容的提问来源于stack exchange,提问作者nanakondor
相关产品推荐
相关产品推荐

