1 JSON 与 BSON
MongoDB 是文档数据库,什么是文档呢?如果你看了之前的文章相信你已经有些概念了,这里的文档不是指 Word、PDF 这样的文档,而是类似 JSON(JavaScript Object Notation) 的对象,由不同的键以及对应的值组成,它的名字是 BSON(Binary JSON)。
BSON 可以理解为 JSON 的扩展,我们先来认识下后者。JSON 是一种轻量的数据交换格式,常用于进程间通信、API 返回结果等方面。看个例子
{
"name": "xiaoming",
"age": 18,
"is_student": true,
"favorites": ["basketball", "sing"],
"extra": {
"height": 180,
"country": "china"
}
}
这就是一个 JSON 串,有如下特征
- 以文本形式存储
- 支持的简单类数据型:有字符串、数字、布尔值
- 支持的复合数据类型有:数组、对象
- 支持的特殊数据类型有:null
- JSON 的最外层是一个数组或者对象
对于复合类型我做一些说明:
数组,由 0 到多个元素组成,多个中间以逗号隔开,外层使用方括号([]
)进行包裹,比如 favorites 字段对应的值就是一个数组。数组里面的元素可以是简单类型或者复合类型的一种或多种,也就是说不要求数组里的类型都相同。
对象,由 0 到多个键值对组成,多个中间以逗号隔开,外层使用花括号({}
)进行包裹,比如 extra 字段对应的值就是一个对象。其中的键只能是字符串,值可以是简单类型或者复合类型。这个例子中 JSON 的最外层也是一个对象。
JSON 轻量、易读,这是它的优势,不过也有些缺陷
- 支持的类型不是很精确,比如数字,我们无法判定是整型还是浮点型,如果根据有没有小数点来区分,即使判定是整型了,它是 32 位的还是 64 位的我们也无从得知
- 支持的类型不是很全,比如常用的时间类型没有
这样一来就引入了 BSON,它做了如下扩充
- 数字支持更准确的类型,对于整型有 int32、int64 等,对于小数有 double、decimal128
- 引入了 Timestamp、UTC datetime 等其它类型
需要注意的是,BSON 是以二进制形式存储的,所以可读性不高。另外,它的最外层只能是对象。
PS:如果比较好奇 JSON 和 BSON 的规范,可以看下文末的参考链接 1 和 2
2 MongoDB 中常见的数据类型
官方文档 BSON Types 一节(见参考链接 3)中对支持的数据类型列了一个表,我按自己的理解给大家梳理下。
JSON 支持的数据类型,BSON 肯定也支持,所以先用 JSON 去理解其中一部分数据类型
JSON 中的数据类型 | MongoDB 中的数据类型 | |
---|---|---|
简单类型 | 数字 | - |
布尔 | Boolean | |
字符串 | String | |
复合类型 | 数组 | Array |
对象 | Object | |
特殊类型 | null | Null |
BSON 对数字进行了一些扩充,我们记住常见的几个就行
- 32-bit integer(32 位整数)
- 64-bit integer(64 位整数)
- Double(浮点数)
- Decimal128(小数)
Double 和 Decimal128 有什么差别呢?你可以简单理解为后一种更加准确,前者存储的是一个大概值,后者则是存储一个准确值。比如,同样是 9.99,Decimal128 存储的是准确的,Double 则是存的 9.9900000000000002131628...。一般情况我们使用 Double 没有什么问题,但是在某些对数字准确度有较大要求的场景下就要使用 Decimal128,比如电商、金融行业等。另外,它们所占的存储空间也是不同的,前者是 8 个字节,后者是 16 个字节。
BSON 还扩充了一些类型,我们也挑几个常用的认识下
- ObjectID
该类型主要用于文档唯一 _id
的生成,长度为 12 个字节
- 前 4 个字节记录的是时间戳
- 中间 5 个字节是随机数
- 后 3 个字节是计数器
MongoDB 3.2 以及之前的版本对于中间 5 个字节的生成策略有些不同:前 3 个是机器 ID,后 2 个是进程 ID。3.2 之后的版本使用的是随机数。
注意:本文在对字节进行前后描述时,前指的是高字节位,后指的低字节位
- Timestamp
该类型主要用在 MongoDB 程序内部,用于标识操作的先后顺序,长度为 8 个字节
- 前 4 个字节记录的是时间戳
- 后 4 个字节记录的是在同一秒内某个操作的序号,这个序号是递增的,由于同一秒内经常会有多个操作,所以采用这种方式记录
- Date
应用如果要用到时间类型的话,一般会使用这个数据类型,它的底层其实是一个 64 位的整数,记录的也是时间戳,不过单位是毫秒。
另外说明下,如果你看过 BSON 的规格描述,你会发现 MongoDB 中对于一些类型的命名和 BSON 有些差异。比如,MongoDB 官方文档中 Object 其实和 BSON 中的 document 是同一种数据类型,只是名字不同。我们不用过于纠结这些,只要记住一种命名,比如 MongoDB 官方文档中的,然后知道它代表着什么或者是怎么存的就行了。
如果需要比较全面的了解 MongoDB 中支持的数据类型,请查阅官方文档 BSON Types 一节(参考链接 3)。
3 使用 Compass 查看数据类型
我们打开 Compass,在集合 video.movies 的详情页面,打开 Schema 标签页,点击 ANALYZE 按钮后等待一小会后,我们可以看到文档中各字段值的数据类型
列下部分字段的数据类型
_id
:ObjectIdcast
:3 种数据类型,有 Array、String 和 Undefined。由于 MongoDB 不会对某个字段的类型做强制要求,也就出现了不同文档同一个字段类型不同的情况。Undefined 表示cast
字段在某些文档中是不存在的viewerRating
:3 种数据类型,有 Double、32-bit integer 和 Undefined
PS:这个小节中,集合的类型信息我都没有列完,大家可以自己使用 Compass 看下,再看下其它集合的一些信息,然后对照前面的介绍熟悉一些常用的数据类型。
需要注意的是,上面的数据类型信息都是取样统计的(一般是取 1000 条,具体信息可以看查询框下的提示信息,比如上图样本量为 1000,总文档量为 963534,比例大概是 0.1%),并非统计的集合中的所有文档,所以不能百分百的保证全面。不过一般情况下这也够了,因为同一个集合中的文档字段大部分是相同的,即使有差异,取样的时候大概率也能取到,所以统计结果还是十分接近于真实情况的。
再看下集合 citibike.trips
也列下部分字段的数据类型
end station location
:Object,其中子文档又有 2 个字段coordinates
:2 种数据类型,有 Array 和 Doubletype
:String
stop time
:Date
字段 end station location
有点特殊,它的内容类似这样
"end station location": {
"type": "Point",
"coordinates": [-73.99973337, 40.71910537]
}
在 MongoDB 中可以以这种方式存储地理位置信息,type 指地理位置类型,coordinates 则指具体的坐标,这个对象在 MongoDB 中有个名字 GeoJSON object,可以理解为 MongoDB 基于 Object 类型扩展出的新的类型,但这个类型并非 BSON 规范所有。
在 Compass 中我们可以启用地理信息可视化的设置,依次点击菜单项 Help -> Privacy Settings,会有弹窗
勾选设置 Enable Geographic Visualizations 后,点击 Close 关闭弹窗,最后点击下 ANALYZE 重新生成统计信息
可以看到,Compass 对取样的文档中的地理位置进行了可视化。细心的伙伴可能会注意到,字段 end station location
下方展示的类型也从 document 变成了 coordinates,和前面 MongoDB 中的数据类型命名和 BSON 不一致一样,我们不用太纠结命名,知道 coordinates 指的是 GeoJSON object 就行了。
4 小结
本文简单介绍了 BSON,然后介绍了 MongoDB 中常用的 BSON 数据类型,最后借助 Compass 工具对这些类型进行了熟悉。
5 参考
- http://bsonspec.org/spec.html
- https://www.json.org/json-en.html
- https://docs.mongodb.com/manual/reference/bson-types