ObjectId
mongoDB中存储的文档必须有一个”_id”键,这个键的值可以是任何类型的,默认是ObjectId对象。在一个集合里面,每个文档都有唯一的”_id”值,来确保集合里面每个文档都能被唯一标识。那么你可能会问,既然ObjectId的作用是用来标识文档,那为什么不用普通递增id呢?后文有解释。
这就需要了解MongoDB的特点,MongoDB是一个基于分布式文件存储的数据库。因此在高并发的场景下,自增id与MongoDB的设计背道而驰。在分布式环境中,自增id要保证正确递增必然会影响效率,因此MongoDB采用ObjectId作为对象标识。ObjectId是bson(binary json)类型的数据,由12位值组成:
- 前4个字节表示时间戳,是文档创建时的时间,它是递增的,可以保证ObjectId总体递增的顺序。
但是上面的递增不是绝对的,因为这个时间只是精确到秒,而同一台机器同一秒内生成的ObjectId还会因pid不同而不同,pid小的ObjectId始终排在pid大的那个前面。 - 接着3位是机器识别码
也就是在同一台机器中,每次生成ObjectId中,这三个字节总相等 - 紧接2位着是进程id的值
- 最后3位是随机值
在机器、进程都确定的情况下,某一秒最多有2^24-1个对象,也就是16777215,从目前机器的性能来看,很难超过这个限制。
了解了这些,其实你自己就可以尝试动手写一个ObjectId生成器。下面看mgo包对其的支持。
ObjectId的生成
package main
import (
"gopkg.in/mgo.v2/bson"
"fmt"
)
func main(){
//生成一个新的ObjectId值
id := bson.NewObjectId()
fmt.Println(id)
//从ObjectId中获取时间戳
idTime := id.Time()
fmt.Println(idTime)
//从ObjectId中获取机器码
idMac := id.Machine()
fmt.Println(idMac)
//从ObjectId中获取进程id
idPid := id.Pid()
fmt.Println(idPid)
//从OjbectId中获取随机数
idCount := id.Counter()
fmt.Println(idCount)
}
执行后的结果如下所示:
ObjectIdHex("5b0c01a2f2fad1839452c0e7")
2018-05-28 21:18:26 +0800 CST
[242 250 209]
33684
5423335