利用mongo的findAndModify原子性操作實現auto increment ID

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

實際情況

預設情況下,mongo使用_id自動產生uniq id,而且在mongo內建的命令裡,無法指定一個自增欄位。自增欄位在多線程時必須是原子性的,這在大資料情況下很難實現伸縮性(scalability)。

Generally in MongoDB, you would not use an auto-increment pattern for
the _id field, or any field, because it does not scale for databases
with large numbers of documents. Typically the default value ObjectId
is more ideal for the _id.

不過好訊息是,_id不一定非得是ObjectId類型,它可以是任何類型。而且,在mongo裡面,有一個findAndModify命令是原子性的。我們可以使用它實現auto increment ID。
在golang的mgo庫裡,Apply命令可以實現findAndModify功能

具體策略和代碼

策略

假設我們要把test表的_id欄位設定為自增的,我們需要先建立一個counter表,這個表裡面儲存一個seq欄位,通過在seq上使用Apply,擷取新doc的seq值作為test表中下一個id的值。

代碼如下:

package mainimport (    "fmt"    "gopkg.in/mgo.v2"    "gopkg.in/mgo.v2/bson"    "log"    "math/rand"    "sync")func init() {    log.SetFlags(log.Lshortfile | log.LstdFlags)}func main() {    session, err := mgo.Dial("usr:pwd@127.0.0.1:27017/dbname")    if err != nil {        log.Fatal("無法開啟MongoDB!")        return    }    defer session.Close()    clt := session.DB("dbname").C("test")    getNextSeq := func() func() int {        counter := session.DB("dbname").C("counter")        cid := "counterid"        return func() int {            change := mgo.Change{                Update:    bson.M{"$inc": bson.M{"seq": 1}},                Upsert:    true,                ReturnNew: true,            }            doc := struct{ Seq int }{}            if _, err := counter.Find(bson.M{"_id": cid}).Apply(change, &doc); err != nil {                panic(fmt.Errorf("get counter failed:", err.Error()))            }            log.Println("seq:", doc)            return doc.Seq        }    }()    wg := sync.WaitGroup{}    // 建立10個 go routine類比多線程環境    for i := 0; i < 10; i++ {        wg.Add(1)        go func(i int) {            _row := map[string]interface{}{                "_id":       getNextSeq(),                "username":  fmt.Sprintf("name%2d", i),                "telephone": fmt.Sprintf("tel%4d", rand.Int63n(1000))}            if err := clt.Insert(_row); err != nil {                log.Printf("insert %v failed: %v\n", _row, err)            } else {                log.Printf("insert: %v success!\n", _row)            }            wg.Done()        }(i)    }    wg.Wait()}

輸出:

2016/04/28 17:05:28 main.go:37: seq: {201}2016/04/28 17:05:28 main.go:54: insert: map[_id:201 username:name 9 telephone:tel 410] success!2016/04/28 17:05:28 main.go:37: seq: {204}2016/04/28 17:05:28 main.go:37: seq: {203}2016/04/28 17:05:28 main.go:37: seq: {202}2016/04/28 17:05:28 main.go:37: seq: {206}2016/04/28 17:05:28 main.go:37: seq: {205}2016/04/28 17:05:28 main.go:54: insert: map[_id:202 username:name 5 telephone:tel  51] success!2016/04/28 17:05:28 main.go:54: insert: map[_id:204 username:name 2 telephone:tel 551] success!2016/04/28 17:05:28 main.go:54: insert: map[telephone:tel 821 _id:203 username:name 1] success!2016/04/28 17:05:28 main.go:37: seq: {207}2016/04/28 17:05:28 main.go:54: insert: map[_id:205 username:name 6 telephone:tel 320] success!2016/04/28 17:05:28 main.go:54: insert: map[_id:206 username:name 4 telephone:tel 937] success!2016/04/28 17:05:28 main.go:54: insert: map[_id:207 username:name 3 telephone:tel 758] success!2016/04/28 17:05:28 main.go:37: seq: {208}2016/04/28 17:05:28 main.go:54: insert: map[username:name 7 telephone:tel 148 _id:208] success!2016/04/28 17:05:28 main.go:37: seq: {209}2016/04/28 17:05:28 main.go:54: insert: map[_id:209 username:name 0 telephone:tel 216] success!2016/04/28 17:05:28 main.go:37: seq: {210}2016/04/28 17:05:28 main.go:54: insert: map[_id:210 username:name 8 telephone:tel 449] success!

mongo查詢結果

db.test.find({_id:{$gt:200}}){ "_id" : 201, "username" : "name 9", "telephone" : "tel 410" }{ "_id" : 202, "username" : "name 5", "telephone" : "tel  51" }{ "_id" : 203, "username" : "name 1", "telephone" : "tel 821" }{ "_id" : 204, "username" : "name 2", "telephone" : "tel 551" }{ "_id" : 205, "username" : "name 6", "telephone" : "tel 320" }{ "_id" : 206, "username" : "name 4", "telephone" : "tel 937" }{ "_id" : 207, "username" : "name 3", "telephone" : "tel 758" }{ "_id" : 208, "username" : "name 7", "telephone" : "tel 148" }{ "_id" : 209, "username" : "name 0", "telephone" : "tel 216" }{ "_id" : 210, "username" : "name 8", "telephone" : "tel 449" }

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.