MongoDB ObjectId
ObjectId介绍
在前面的学习中中我们已经使用了MongoDB 的对象 Id(ObjectId)。
接下来,我们将了解的ObjectId的结构。
ObjectId 是一个12字节 BSON 类型数据,有以下格式:
前4个字节表示时间戳
接下来的3个字节是机器标识码
紧接的两个字节由进程id组成(PID)
最后三个字节是随机数。
MongoDB中存储的文档必须有一个”_id”键。这个键的值可以是任何类型的,默认是个ObjectId对象。
在一个集合里面,每个文档都有唯一的”_id”值,来确保集合里面每个文档都能被唯一标识。
MongoDB采用ObjectId,而不是其他比较常规的做法(比如自动增加的主键)的主要原因,因为在多个 服务器上同步自动增加主键值既费力还费时。
创建新的ObjectId
|
|
你也可以使用生成的id来取代MongoDB自动生成的ObjectId:
|
|
创建文档的时间戳
由于 ObjectId 中存储了 4 个字节的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:
|
|
ObjectId 转换为字符串
在某些情况下,您可能需要将ObjectId转换为字符串格式:
MongoDB 自动增长
MongoDB 没有像 SQL 一样有自动增长的功能, MongoDB 的 _id 是系统自动生成的12字节唯一标识。
但在某些情况下,我们可能需要实现 ObjectId 自动增长功能。
由于 MongoDB 没有实现这个功能,我们可以通过编程的方式来实现,以下我们将在 counters 集合中实现_id字段自动增长。
举个栗子:
使用 counters 集合
首先创建 counters 集合:
|
|
然后我们向 counters 集合中插入以下文档,使用 productid 作为 key,sequence_value 字段是序列通过自动增长后的一个值:
|
|
MongoDB 原子操作
写函数之前我们首先来看一下MongoDB的原子操作
众所周知,Redis采用的是异步I/O非阻塞的单进程模型,每一条Redis命令都是原子性的,而MongoDB也为我们提供了一些原子操作
对单个文档进行原子性修改
mongoDB保证了对单个document的多个filed的原子性修改。如果需要对单个文档进行原子性的CAS操作(check and set),可以使用findAndModify操作。
比如下面就是一条原子性的CAS操作,首先选择_id为123的文档(注意这里只选择了一个文档),然后对计数器count加1,将status字段变为true,并返回修改后的结果。
|
|
对多个文档使用$isolate操作符
$isolate操作符可以对多个文档的修改提供隔离性。针对其他线程的并发写操作,$isolate保证了提交前其他线程无法修改对应的文档。针对其他线程的读操作,$isolate保证了其他线程读取不到未提交的数据。
但是$isolate有验证的性能问题,因为这种情况下线程持有锁的时间较长,严重的影响mongo的并发性。另外,$isolate也无法保证多个文档修改的一致性(all-or-nothing),$isolate失败是可能只修改了部分文档。
从语义层面实现事务性操作
mongoDB官方提供了一种做法,即两阶段提交(two-phase commit),基本的原理就是利用了写操作的幂等性。具体实现可以看官网的详细讲解。但是利用幂等性来实现事务性有一个重要的前置条件:业务不在乎中间态的不一致。幂等性可以保证最终的一致性,但是会出现中间的不一致状态。
原子操作栗子
先插入我们数据
|
|
你可以使用 db.collection.findAndModify() 方法来判断书籍是否可结算并更新新的结算信息
在同一个文档中嵌入的 available 和 checkout 字段来确保这些字段是同步更新的:
|
|
原子操作常用命令
- $set
用来指定一个键并更新键值,若键不存在并创建
|
|
- $unset
用来删除一个键
|
|
- $inc
$inc可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作
|
|
- $push
把value追加到field里面去,field一定要是数组类型才行,如果field不存在,会新增一个数组类型加进去
|
|
- $pushAll
同$push,只是一次可以追加多个值到一个数组字段内
|
|
- $pull
从数组field内删除一个等于value值。
|
|
- $addToSet
增加一个值到数组内,而且只有当这个值不在数组内才增加。
- $pop
删除数组的第一个或最后一个元素
|
|
- $rename
修改字段名称
|
|
- $bit
位操作,integer类型
|
|
可以看到,大部分我们上一篇介绍到的操作都是原子操作
创建JavaScript函数
我们创建函数 getNextSequenceValue 来作为序列名的输入,指定的序列会自动增长 1 并返回最新序列值。在这里序列名为 productid ,并且findAndModify就是一个原子操作
|
|
使用JavaScript函数
|
|
可以发现,_id实现了自动增长
|
|