事务
MongoDB没有事务。
为了扩展性MongoDB取消了事务,主要是多服务器上的分布式事务问题,如果多服务器保证事务,需要引入很多其他和数据库本身不相关的中间件和逻辑。当然不能说分布式事务有什么问题,而是这是MongoDB的设计理念。
那如何实现关系型数据库的事务操作呢?这说到底是一种原子性。在MongoDB中,为了保证操作字段的原子性,有以下几种方案。
首先看一个解决方案,感觉几种事务解决方案其实都是这个思想。
首先MongoDB的命令是原子性的,包括
$set
{ $set : { field : value } }
$unset
{ $unset : { field : 1} }
$inc
$inc可以对文档的某个值为数字型(只能为满足要求的数字)的键进行增减的操作。
{ $inc : { field : value } }
$push
把value追加到field里面去,field一定要是数组类型才行,如果field不存在,会新增一个数组类型加进去。
{ $push : { field : value } }
$pushAll
同$push,只是一次可以追加多个值到一个数组字段内。
{ $pushAll : { field : value_array } }
$pull
从数组field内删除一个等于value值。
{ $pull : { field : _value } }
$addToSet
增加一个值到数组内,而且只有当这个值不在数组内才增加。
$pop
删除数组的第一个或最后一个元素
{ $pop : { field : 1 } }
$rename
修改字段名称
{ $rename : { old_field_name : new_field_name } }
$bit
位操作,integer类型
{$bit : { field : {and : 5}}}
偏移操作符
设置标志位
我们要通过原子性命令,实现多文档更新。例如有如下文档
book = {
_id: 123456789,
title: "MongoDB: The Definitive Guide",
author: [ "Kristina Chodorow", "Mike Dirolf" ],
published_date: ISODate("2010-09-24"),
pages: 216,
language: "English",
publisher_id: "oreilly",
available: 3,
checkout: [ { by: "joe", date: ISODate("2012-10-15") } ]
}
比一般的文档多了两个字段:available、checkout。这就是设置标志位法。
使用 db.collection.findAndModify() 方法来判断书籍是否可结算并更新新的结算信息。
db.books.findAndModify ( {
query: {
_id: 123456789,
available: { $gt: 0 }
},
update: {
$inc: { available: -1 },
$push: { checkout: { by: "abc", date: new Date() } }
}
} )
作业队列
占位
二阶段提交
二阶段提交的算法思路可以概括为: 参与者将操作成败通知协调者,再由协调者根据所有参与者的反馈情报决定各参与者是否要提交操作还是中止操作。
协调者的出现是为了解决事务只知道与其关联的事务事务是否成功,但不知道其他人的事务是否成功。协调者是作为旁观者来看所有事务是否执行成功。
第一阶段提交请求阶段
协调者节点向所有参与者节点询问是否可以执行提交操作,并开始等待各参与者节点的响应。
参与者节点执行询问发起为止的所有事务操作,并将Undo信息和Redo信息写入日志。
各参与者节点响应协调者节点发起的询问。如果参与者节点的事务操作实际执行成功,则它返回一个"同意"消息;如果参与者节点的事务操作实际执行失败,则它返回一个"中止"消息。
有时候,第一阶段也被称作投票阶段,即各参与者投票是否要继续接下来的提交操作。
第二阶段提交执行阶段
成功
当协调者节点从所有参与者节点获得的相应消息都为"同意"时:
协调者节点向所有参与者节点发出"正式提交"的请求。
参与者节点正式完成操作,并释放在整个事务期间内占用的资源。
参与者节点向协调者节点发送"完成"消息。
协调者节点收到所有参与者节点反馈的"完成"消息后,完成事务。
失败
如果任一参与者节点在第一阶段返回的响应消息为"终止",或者 协调者节点在第一阶段的询问超时之前无法获取所有参与者节点的响应消息时:
协调者节点向所有参与者节点发出"回滚操作"的请求。
参与者节点利用之前写入的Undo信息执行回滚,并释放在整个事务期间内占用的资源。
参与者节点向协调者节点发送"回滚完成"消息。
协调者节点收到所有参与者节点反馈的"回滚完成"消息后,取消事务。
有时候,第二阶段也被称作完成阶段,因为无论结果怎样,协调者都必须在此阶段结束当前事务。
Log Reconciliation
版本控制
其实也是增加一个版本字段。更新时判断版本值,并incr一下。
PS.我觉得事务控制很麻烦,关于二阶段提交还没弄太明白。不过既然麻烦就让MongoDB做更适合它的事情比较好。
MoongoDB中的正则
MongoDB 使用 $regex 操作符来设置匹配字符串的正则表达式。
{
"text": "enjoy the mongodb articles",
"tags": [
"mongodb",
"nosql"
]
}
>db.message.find({text:{$regex:"mongodb"}})
或
>db.message.find({post_text:/mongodb/})
如果检索需要不区分大小写,我们可以设置 $options 为 $i。
数组元素使用正则表达式
>db.message.find({tags:{$regex:"run"}})
$regex操作符的使用
KaTeX parse error: Expected '}', got 'EOF' at end of input: …忽略大小写,{<field>{regex/pattern/i}},设置i选项后,模式中的字母会进行大小写不敏感匹配。
m 多行匹配模式,{{ r e g e x / p a t t e r n / , regex/pattern/, regex/pattern/,options:‘m’},m选项会更改^和KaTeX parse error: Expected '}', got 'EOF' at end of input: …空白字符,{<field>:{regex:/pattern/,KaTeX parse error: Expected 'EOF', got '}' at position 12: options:'m'}̲,设置x选项后,正则表达式中的…regex:/pattern/,KaTeX parse error: Expected 'EOF', got '}' at position 12: options:'s'}̲,设置s选项后,会改变模式中的…regex操作符时,需要注意下面几个问题:
i,m,x,s可以组合使用,例如:{name:{ r e g e x : / j ∗ k / , regex:/j*k/, regex:/j∗k/,options:“si”}}
在设置索弓}的字段上进行正则匹配可以提高查询速度,而且当正则表达式使用的是前缀表达式时,查询速度会进一步提高,例如:{name:{$regex: /^joe/}
正则部分不纠结过多语法,一是用到可以查,二是第三方有封装。
Capped Collections
固定长度集合,就是一个定长环形队列。
在32位机子上一个cappped collection的最大值约为482.5M,64位上只受系统文件大小的限制。
用法
- 储存日志信息
- 缓存一些少量的文档
db.createCollection("cappedLogCollection",{capped:true,size:10000,max:1000})
size 是整个集合空间大小,单位为【KB】
max 是集合文档个数上线,单位是【个】
如果空间大小到达上限,则插入下一个文档时,会覆盖第一个文档;如果文档个数到达上限,同样插入下一个文档时,会覆盖第一个文档。两个参数上限判断取的是【与】的逻辑。
GridFS
MongoDB存储大文件的一种方式。将大文件拆分成多个子文件存储,用一个_id标识归类。这样可以不用加载整个大文件而访问那其中某一部分文件。MongoDB的BSON格式的数据(文档)存储有尺寸限制,最大为16M。GridFS有两部分,fs.files与fs.chunks,文件的实际内容被存在chunks中。和文件有关的meta data放在fs.files中。
>mongofiles.exe -d gridfs put song.mp3
>db.fs.files.find()
{
_id: ObjectId('534a811bf8b4aa4d33fdf94d'),
filename: "song.mp3",
chunkSize: 261120,
uploadDate: new Date(1397391643474), md5: "e4f53379c909f7bed2e9d631e15c1c41",
length: 10401959
}
使用下面的命令查找所有chunks
>db.fs.chunks.find({files_id:ObjectId('534a811bf8b4aa4d33fdf94d')})
删除chunks并不会释放内存。想要进行内存回收,只能备份数据库,删除后重新恢复,或db.repairDatabase()。
当使用db.repairDatabase()命令没有足够的磁盘剩余空间时,可以采用dump & restore方式回收磁盘资源。如果MongoDB是副本集模式,dump & restore方式可以做到对外持续服务,在不影响MongoDB正常使用下回收磁盘资源。
MogonDB使用副本集, 实践使用dump & restore方式,回收磁盘资源。70G的数据在2小时之内完成数据清理及磁盘回收,并且整个过程不影响MongoDB对外服务,同时可以保证处理过程中数据库增量数据的完整。
MongoDB管理工具
Navicat for mongodb