Mgo 库的常见坑总结

mgo 库 是一个很好用的

驱动。对我们来说,主力数据库是 ```MongoDB```,因此这个驱动对我们来说也是非常重要的。但是,mgo 库有些问题算是一些坑,这里我做了一些简单的整理。
1
2
3
4
5
6
7
8
9
10
11
12

## 一些关于 bson.ObjectId 的问题

### ObjectId 为空的判断
如果你看 ```bson.ObjectId``` 定义的话,它是一个 ```string``` 类型的数据。但是如果你直接定义一个结构,并且生成对象时,这个对象并不是这样的。

我们首先定义一个结构体:

```go
type Home struct {ID bson.ObjectId `bson:"_id,omitempty"`
Name string `bson:"name"`
}

然后看看一个生成的内容

1
2
h := Home{Name:"123"}
fmt.Println(h.ID)

结果是

```ObjectId``` 是否为空,使用 ```h.ID
1
2
3
4
5

```go
h := Home{Name:"123"}
fmt.Println(h.ID)
fmt.Println(h.ID.Hex() == "")

正确的生成 ObjectId

首先值得注意的是

time.Time)``` 这个方法生成的 ```ObjectId``` 并不是唯一的,结果可能导致的是插入失败。最有效的方式是设定 ```ObjectId``` 对象支持 ```omitempty``` 属性,就像我上面生成的结构体一样,由数据库统一调配生成 ObjectId。如果真的确实需要,可以选择 ```NewObjctId()```。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20

## 时间问题

之前看到有人问,为什么保存的时间进入到数据库中慢了 8 个小时呢?原因是在保存进入 ```MongoDB``` 时,数据是按照 ```UTC``` 时间(不懂什么是 UTC?[看这里](https://zh.wikipedia.org/zh/%E5%8D%8F%E8%B0%83%E4%B8%96%E7%95%8C%E6%97%B6))进行的保存,但是取出是按照当前时区来取出。那么问题来了,我的客户如果不都是国人,我怎么保存时间呢?目前我们采用了两种方式来确定数据库的保存时间。一种是 ```Unix 时间戳 ```,这个是不受到时区的影响的,由前端格式化为对应的时区时间;另外一种则是需要在额外的对从 ```MongoDB``` 数据库中取出的数据进行额外的时区校准,简单来说可以这样:

```go
type Home struct {ID bson.ObjectId `bson:"_id,omitempty"`
Name string `bson:"name"`
InsertTime time.Time `bson:"insert_time"`
}

func main() {sess, _ := mgo.Dial("127.0.0.1")
c := sess.DB("test").C("home")
h := Home{Name:"123", InsertTime: time.Now()}
c.Upsert(bson.M{"name":"123"}, h)
c.Find(bson.M{"name":"123"}).One(&h)
fmt.Println(h.InsertTime.Format("2006-01-02 15:04:05"))
tz, _ := time.LoadLocation("America/New_York")
fmt.Println(h.InsertTime.In(tz).Format("2006-01-02 15:04:05"))
}

更高效的使用 Session

中合理使用 ```Session``` 可以更高效的操作数据库,做法是在之前进行一次 ```Copy``` 操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

```go
sessionCopy := mongoSession.Copy()
defer sessionCopy.Close()
collection := sessionCopy.DB(TestDatabase).C("buoy_stations")

log.Printf("RunQuery : %d : Executing\n", query)

// Retrieve the list of stations.
var buoyStations []BuoyStation
err := collection.Find(nil).All(&buoyStations)
if err != nil {log.Printf("RunQuery : ERROR : %s\n", err)
return
}

log.Printf("RunQuery : %d : Count[%d]\n", query, len(buoyStations))

另外值得一提的是 MongoDB 本身,目前我们已经提升到了 MongoDB 3.0+ 版本,优势是相对之前版本的 WiredTiger 引擎比较令人印象深刻(如果有条件可以选择最新的 3.2 版本)。大家对之前 MongoDB 中索引建立的痛苦有印象,这个现象在 WiredTiger 引擎中也有所改进。更多的改进可以在 WiredTiger官网 和 MongoDB 的 性能白皮书 中了解更多。最后,当然,我仍然不建议在非严重必要情况下创建多余索引,这对 MongoDB 的性能和资源消耗都有较大影响。