#2 Mongo 基础
DB MongoDB 2018-09-05版本
以下是几个大版本和发布时间,作为一个大概的时间线吧:
2009/12 1.2
2010/03 1.4
2010/08 1.6 分片(支持水平拓展)
2011/03 1.8
2011/09 2.0 GridFS
2012/08 2.2 Aggregation Framework
2013/03 2.4 全文搜索
2014/04 2.6 WiredTiger 存储引擎
2015/03 3.0
2015/12 3.2 Change Streams
2016/11 3.4 多文档事务
2017/11 3.6
2018/06 4.0
2019/08 4.2
2020/07 4.4
2021/07 5.0 时间序列 + 聚簇索引(Clustered Indexing)+ 实时重分片 (Live Resharding) + 版本化 API (Versioned API)
2022/07 6.0
2023/08 7.0 可查询加密技术(Queryable Encryption)
2024/10 8.0
2025/09 8.2 功能完善与性能提升
当前最新版本 8 月发布的 4.0.2
Update @ 2021/06/07:
之后主版本号就一直停在了 4,2020 年之后甚至一直停在了 4.4(2019 年 4.2,2020 年 4.4),这也意味着功能组件稳定下来了。
#1 MongoDB 聚集(数据表)中的 ID
DB MongoDB 2016-06-01默认是一个 ObjectId 对象,也可以手动设置。
举个栗子
使用 PyMongo:
# -*- coding: utf-8 -*-
from pymongo import MongoClient
client = MongoClient() # 连接到默认主机的默认端口:localhost:27017
db = client.test_db
collection = db.test_collection
collection.insert({"Hu" : "Ang", "Love" : [5, 'Sun', 'Xiu']})
collection.insert({"And" : 20, "Daughter" : True})
collection.insert({"GIRL": ',', "IS": "A GIRL", '_id': 123})
如果是 MongoDB 数据库操作,就应该是这样:
$ mongo
> use test_db
> db.test_collection.insert({"Hu" : "Ang", "Love" : [5, 'Sun', 'Xiu']})
> db.test_collection.insert({"And" : 20, "Daughter" : True})
> db.test_collection.insert({"GIRL": ',', "IS": "A GIRL", '_id': 123})
最后查到的结果显示如下:
> db.test_collection.find()
{ "_id" : ObjectId("5746c0f900e0990cfc600938"), "Love" : [ 5, "Sun", "Xiu" ], "Hu" : "Ang" }
{ "_id" : ObjectId("5746c0f900e0990cfc600939"), "And" : 20, "Daughter" : true }
{ "_id" : 123, "GIRL" : ",", "IS" : "A GIRL" }
_id
如果自己往里面传 _id 的话,要注意唯一性约束,如果里面存在这个 _id 值,那么就会报错:E11000 duplicate key error index
为什么没有采用像其他数据库一样的主键自增机制?
可能是因为 MongoDB 天生的分布式属性,导致其不愿耗费精力来处理自增主键的同步问题。
ObjectId
关于 ObjectId 字段,官方文档中对每个字节所表示内容的说明:
ObjectId is a 12-byte BSON type, constructed using:
- a 4-byte value representing the seconds since the Unix epoch,
- a 3-byte machine identifier,
- a 2-byte process id, and
- a 3-byte counter, starting with a random value.
ObjectId 占 12 个字节,其中:
- 第 1、2、3、4 个字节用来存 Unix 时间戳
- 第 5、6、7 个字节用来存机器标识
- 第 8、9 个字节用来存客户端进程编号
时间戳 + 机器标识 + 客户端进程编号 保证 “机器 + 进程 + 时间” 的一致性。 - 第 10、11、12 个字节用来存随机字符串
保证同一台机器,同一个客户端进程,在一秒种之内创建的记录的一致性。
2 *_ (8 _ 3) = 16777216,也就是说,理论上,同一台机器,同一个客户端进程,在一秒种之内可以创建 1677 万多条记录。
举个例子,比如在 ObjectId("5746c0f900e0990cfc600939") 中 5746c0f9 就是时间戳,00e099 就是机器标识,0cfc 就是客户端进程编号,600939 就是随机字符串。
通过这个设计,保证不同机器的 mongod 服务、同一个机器上的不同 mongod 服务进程之间都不出现重复值的情况(可能性极低,如果出现,可能也有后续的处理办法)。
重点:ObjectId 在客户端生成!!!
我个人也觉得 ObjectId 在客户端生成比服务器端要好:
- 更加容易根据机器标识 + 进程编号保证记录的唯一性
- 将生成 ObjectId 的这一部分计算转移出去,也能略微减轻 MongoDB 服务的计算压力。
- 客户端插入记录的时候,自己就知道 ID,不需要服务器端的反馈,针对这个设计可以设计出一些不需要返回的 insert 方法,给服务器减少一些查询带来的压力。
PyMongo 中就是使用 bson.objectid.ObjectId 生成的。可以阅读一下相关代码,了解这个 ID 的生成方法。
PS:比如,在我的 Ubuntu 环境中,代码文件就是 /usr/local/lib/python2.7/dist-packages/bson/objectid.py。
参考
- MongoDB 官方文档(v2.6),Data Models > Data Model Reference > ObjectId