2016-12-06 114 views
4

我的數據層使用Mongo聚合體面的金額,平均來說,查詢需要500-650ms才能返回。我正在使用mgomgo - 查詢性能似乎一直很慢(500-650ms)

下面顯示了一個示例查詢函數,它代表了我的大多數查詢的樣子。

func (r userRepo) GetUserByID(id string) (User, error) { 
    info, err := db.Info() 
    if err != nil { 
     log.Fatal(err) 
    } 

    session, err := mgo.Dial(info.ConnectionString()) 
    if err != nil { 
     log.Fatal(err) 
    } 
    defer session.Close() 

    var user User 
    c := session.DB(info.Db()).C("users") 
    o1 := bson.M{"$match": bson.M{"_id": id}} 
    o2 := bson.M{"$project": bson.M{ 
     "first":   "$first", 
     "last":   "$last", 
     "email":   "$email", 
     "fb_id":   "$fb_id", 
     "groups":   "$groups", 
     "fulfillments": "$fulfillments", 
     "denied_requests": "$denied_requests", 
     "invites":   "$invites", 
     "requests": bson.M{ 
      "$filter": bson.M{ 
       "input": "$requests", 
       "as": "item", 
       "cond": bson.M{ 
        "$eq": []interface{}{"$$item.active", true}, 
       }, 
      }, 
     }, 
    }} 
    pipeline := []bson.M{o1, o2} 
    err = c.Pipe(pipeline).One(&user) 
    if err != nil { 
     return user, err 
    } 
    return user, nil 
} 

user結構我有類似以下..

type User struct { 
    ID    string  `json:"id" bson:"_id,omitempty"` 
    First   string  `json:"first" bson:"first"` 
    Last   string  `json:"last" bson:"last"` 
    Email   string  `json:"email" bson:"email"` 
    FacebookID  string  `json:"facebook_id" bson:"fb_id,omitempty"` 
    Groups   []UserGroup `json:"groups" bson:"groups"` 
    Requests  []Request  `json:"requests" bson:"requests"` 
    Fulfillments []Fulfillment `json:"fulfillments" bson:"fulfillments"` 
    Invites  []GroupInvite `json:"invites" bson:"invites"` 
    DeniedRequests []string  `json:"denied_requests" bson:"denied_requests"` 
} 

根據我提供什麼,有什麼顯而易見的,爲什麼我的查詢平均500-650ms,將建議?

我知道我可能通過使用聚合管道吞嚥了一點性能命中,但我不希望它會這麼糟糕。

+0

你有'requests.active'上的索引嗎? – RickyA

+0

查詢計劃是什麼樣的? ('db.collection.find({$ query:{},$ explain:1})') – RickyA

回答

10

..是否有任何明顯的跡象表明我的查詢者平均爲500-650ms的原因?

是的,有。在執行每個查詢之前,您正在調用mgo.Dial()mgo.Dial()必須每次都連接到MongoDB服務器,在查詢後關閉。連接可能需要幾百毫秒才能建立,包括認證,分配資源(在服務器端和客戶端)等等,這是非常浪費的。

這種方法通常只對給定的集羣調用一次。然後在獲得的會話上使用New或Copy方法建立到同一集羣的更多會話。這將使他們共享底層集羣,並適當地管理連接池。

創建一個全局會話變量,在啓動時連接一次(使用例如包init()功能),並使用該會話(或它的一個副本/克隆,通過Session.Copy()Session.Clone()獲得)。 例如:

var session *mgo.Session 
var info *db.Inf // Use your type here 

func init() { 
    var err error 
    if info, err = db.Info(); err != nil { 
     log.Fatal(err) 
    } 
    if session, err = mgo.Dial(info.ConnectionString()); err != nil { 
     log.Fatal(err) 
    } 
} 

func (r userRepo) GetUserByID(id string) (User, error) { 
    sess := session.Clone() 
    defer sess.Close() 

    // Now we use sess to execute the query: 
    var user User 
    c := sess.DB(info.Db()).C("users") 
    // Rest of the method is unchanged... 
} 
+0

這會將平均時間縮短到〜75ms :)感謝您指出這一點,我感覺自己在做一件嚴厲的事情錯誤。 – TheJediCowboy

+0

這甚至不適用於壓力很大的應用程序:https://github.com/go-mgo/mgo/issues/473 –