美文网首页
【Go - 超实用,3行代码实现个自增器】

【Go - 超实用,3行代码实现个自增器】

作者: wn777 | 来源:发表于2024-09-14 13:24 被阅读0次

场景

自增器的作用是生成一个唯一的递增序列号。这在一些需要生成自增id的场景十分有用,比如自增的订单号,任务号,序列号。

要点

  • 全局统一:在整个服务体系下,多个服务或者进程,都统一调用这个自增器,来获取自增ID。
  • 严格自增:避免竞争,写冲突造成写覆盖等,导致不严格自增

实现

根据上面要点,需要跨服务进程可以访问,且保障严格自增。综上考虑, 依赖MonogoDB来实现这个自增器,以下是代码实现,

代码

mongodb-conn.go

package main

import (
    "context"
    "os"
    "sync"

    mongo "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

var (
    singletonMongoClient *mongo.Client
    once                 sync.Once
)

var MONGODB_URI = os.Getenv("MONGODB_URI")
var MONGODB_DATABASE = os.Getenv("MONGODB_DATABASE")

func getSingletonMongoClient() *mongo.Client {

    once.Do(func() {
        // 创建连接到 MongoDB 的客户端
        uri := MONGODB_URI
        client, err := mongo.Connect(context.TODO(), options.Client().
            ApplyURI(uri))

        if err != nil {
            panic(err)
        }
        singletonMongoClient = client
    })
    return singletonMongoClient
}
func GetMongoConn() *mongo.Database {
    return getSingletonMongoClient().Database(MONGODB_DATABASE)
}

main.go

package main

import (
    "context"

    bson "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/mongo/options"
)

type UniqSeq struct {
    Key string `bson:"key"`
    Seq int64  `bson:"seq"`
}

func GetNextSeq(key string) (int64, error) {
    conn := GetMongoConn()
    var collectionName = "uniqseqz"
    collection := conn.Collection(collectionName)
    filter := bson.M{"key": key}
    update := bson.M{"$inc": bson.M{"seq": 1}}
    opts := options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After)

    var result UniqSeq
    err := collection.FindOneAndUpdate(context.Background(), filter, update, opts).Decode(&result)

    if err != nil {
        return 0, err
    }
    return result.Seq, nil
}

func main() {
    // Test
    key := "T"
    seq, err := GetNextSeq(key)
    if err != nil {
        panic(err)
    }
    println(seq)
}

这里的核心就在下面三行

// 这里是借助了MongoDB $inc 和 upsert特性
// 先按key过滤,找出对应的document,然后upsert,没有就插入一条,有就加一。
// 以此来完成了自增
filter := bson.M{"key": key}
update := bson.M{"$inc": bson.M{"seq": 1}}
opts := options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After)

关于竞争,

// 这个操作调用的时候,MongoDB Server端会加锁执行,保障操作的原子性,
// 对于调用方可以看以下一行是原子性的,不用担心写冲突或者写覆盖。
var result UniqSeq
    err := collection.FindOneAndUpdate(context.Background(), filter, update, opts).Decode(&result)

运行

最后可以带入运行一下,

MONGODB_URI="${YOUR_MONGODB_URL}" MONGODB_DATABASE="${YOUR_MONGODB_DATABASE}" go run mongodb-conn.go main.go

相关文章

网友评论

      本文标题:【Go - 超实用,3行代码实现个自增器】

      本文链接:https://www.haomeiwen.com/subject/kmpbljtx.html