場景
自增器的作用是生成一個(gè)唯一的遞增序列號玲献。這在一些需要生成自增id的場景十分有用殉疼,比如自增的訂單號梯浪,任務(wù)號,序列號瓢娜。
要點(diǎn)
- 全局統(tǒng)一:在整個(gè)服務(wù)體系下挂洛,多個(gè)服務(wù)或者進(jìn)程,都統(tǒng)一調(diào)用這個(gè)自增器眠砾,來獲取自增ID虏劲。
- 嚴(yán)格自增:避免競爭,寫沖突造成寫覆蓋等褒颈,導(dǎo)致不嚴(yán)格自增
實(shí)現(xiàn)
根據(jù)上面要點(diǎn)柒巫,需要跨服務(wù)進(jìn)程可以訪問,且保障嚴(yán)格自增谷丸。綜上考慮堡掏, 依賴MonogoDB來實(shí)現(xiàn)這個(gè)自增器,以下是代碼實(shí)現(xiàn)刨疼,
代碼
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() {
// 創(chuàng)建連接到 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過濾泉唁,找出對應(yīng)的document,然后upsert揩慕,沒有就插入一條亭畜,有就加一。
// 以此來完成了自增
filter := bson.M{"key": key}
update := bson.M{"$inc": bson.M{"seq": 1}}
opts := options.FindOneAndUpdate().SetUpsert(true).SetReturnDocument(options.After)
關(guān)于競爭迎卤,
// 這個(gè)操作調(diào)用的時(shí)候贱案,MongoDB Server端會(huì)加鎖執(zhí)行,保障操作的原子性止吐,
// 對于調(diào)用方可以看以下一行是原子性的宝踪,不用擔(dān)心寫沖突或者寫覆蓋。
var result UniqSeq
err := collection.FindOneAndUpdate(context.Background(), filter, update, opts).Decode(&result)
運(yùn)行
最后可以帶入運(yùn)行一下碍扔,
MONGODB_URI="${YOUR_MONGODB_URL}" MONGODB_DATABASE="${YOUR_MONGODB_DATABASE}" go run mongodb-conn.go main.go