Posts Tagged ‘ mongoDB

一段小脚本,将 MongoDB 中所有 DB 的 Collection 按大小排序。

最近接到的一个遗留项目,数据库用的是 MongoDB,年久(?)失修,磁盘满了运维报怨。

实例里面有多个库,每个库中又有很多 collection
于是想要看看哪个 collection 占用的空间比较大,想 remove 掉一些过期数据。

发现 pymongo 取出 collection 之后,无法像 mongo 客户端一样可以直接用 collection.stats() 的方式获取状态。
于是很郁闷。

登上 mongo 查看了一下:

?View Code JAVASCRIPT
// db.col.stats
function ( scale ){
return this._db.runCommand( { collstats : this._shortName , scale : scale } )
}
 
// db.stats
function (scale){
return this.runCommand( { dbstats : 1 , scale : scale } );
}

这才发现,原来 db.state 和 collection.state 都是通过 mongodb 的内部命令实现的。

所以就有了如下代码:

?View Code PYTHON
from pymongo import Connection
db = Connection(HOST, PORT)</code>
 
print sorted(
    [(
        "%s -&gt; %s" % (dbn, coln),
        db[dbn].command('collstats', coln)['storageSize']
    )
    for dbn in db.database_names()
    for coln in db[dbn].collection_names()
    ],
    key=lambda x:x[1])

PS:这篇日志是 惰性的 Blade 大大两年之后头一次在这个布珞阁更新,大家鼓励他一下吧。

MongoDB的备份和恢复

话说怎么备份本身就是一个非常值得深入探讨的问题。能把数据放置到别的地方,而后再拿回来使用,看似很简单,但实际应用中却是非常令人恼火的一件事儿。因为实际运行中,所有的数据都是流动的,以刻舟求剑的方式来作数据的备份和恢复就很不靠谱了。但同时,任何应用于生产环境的数据存储方案首先要考虑的就是备份融灾问题。可能你的机房里不会有大象,但是说不定会有老鼠呢。可能你的操作人员不小心拉错了闸。这些事情貌似都不太可能发生。但是以我的实际经验来看,如果你没有备份的话,倒霉的事儿发生的几率就会成几何级数增长。也就是说备份了就备份了,要是不备份,倒霉的事儿总会发生在你头上。所以还是小心一些。另外说一句,备份是非常不符合精益思想的,起码原生态的简单备份有着明显的浪费,而且若是做不好监控的话,出故障的几率并不低,所以千万不要觉得备份就万事大吉,仅仅是运维的第一步而已。

有两种比较简单的备份方法(直接copy数据库文件,mongodump),不太能单独在生产环境中使用。比较建议的做法是构建Master-Slave,在Slave上做些备份,slavedelay是个非常有用参数,要是不想搞得很麻烦,就可以设置几个不同的slavedelay值的slave,这样就可以应对很多情况了。有人问了,如果有些延时,那么岂不是会丢失一些数据。说对了。首先备份并不能保证完全不丢失数据,任何地球上已知的方法都不能保障这一点。其次,备份数据也要考虑备份的目的。其实并不是所有情况都是要回复到最新的数据的,想一想有人用大量垃圾数据填充你的数据库你怎么办。应对这样的情况就不能简单地恢复到最新的备份了是吧。

在slave上,可以定期地加锁,备份,然后解锁。总之了,要在slave展开热备份的一切操作,甚至为备份建立专门的slave。比如视觉中国的图片存储方案中的备份就是这么做的。

索引,做还是不做,这是个问题

先说个题外话很多人都搞错了mongodb的名字,错误地拼成了mangodb,或者mango。我劝大家不要吃芒果了。从我的网站搜索结果来看拼成mangodb的真的不在少数,这里还要请大家特别注意,尤其是母语不是英语的人。否则要是到英语讨论组提问的话,会引起不必要的误解。
首先要说明的是mongodb的索引和传统的关系型数据库的索引几乎完全一样,所以很多原来的优化手段这里也是适用的,反之也对。那么索引是什么呢?有些人觉得我故弄玄虚,玩数据库的谁人不知索引啊。好吧,那我换一个问法,索引用来干什么?索引是为了在查询的时候更加快速。bingo!这句话是有好多隐含的意思的。查询更加快速,那么是和什么相比呢?肯定是和笨方法相比了。所谓的笨方法就是复杂度为O(n)的线性查找了。这种查找的好处就是不用做任何预处理,而且不需要额外的空间,可以容易地并发。据我所知有两种方法可以降低时间复杂度,一种利用排序的方法,可以将查找有序表的时间复杂度降到O(log(n)),一种使用Hash的方法,用空间换时间,使得时间复杂度讲到O(1)。今天要讨论的主要集中在变无序为有序的做法,也就是大部分索引的机理。
既然涉及到排序,那么就涉及到顺序。所以按照{a:1, b:1}建立的索引,和{b:1, a:1}建立的索引是完全不同的。这种不同的表象是建立索引的速度会用不同,深层次的原因就是排序的准则不同所以顺序就不同,导致的排序复杂程度就不一样。要知道排一几乎有序的数列,和排一个几乎无序的数列可能是完全不同的工作量。还记得所有的算法书上都将到了最好情况和最坏情况吧。所以以一定要考虑你自己的数据究竟是什么样子的,本身有什么性质,建立索引的时候要尽量的符合现有顺序。少折腾,会有很大的获益。至于查询的时候,查询优化器不会计较{a:20, b:30}还是{b:30, a:20}会自动优化的。还要考虑的一个问题就是索引的复用,因为如果建立所以是{a:1,b:1,c:1,d:1},下面这几种都将能复用这个索引{a:10}, {a:10,b:20}, {a:10,b:20,c:90},查询关键字的顺序并不重要,但必须是建立索引的前几个关键字的组合,前一个,前两个,以此类推。
上面讲了第一顺序,还有另外一个顺序。那就是关键字的排序顺序。当然要是你的索引只有一个关键字,就没所谓了。要是有多个关键字,首先要考虑关键字摆放的顺序(上面讲到的),然后就是关键字本身怎么排的问题。是{a:1, b:-1}还是{a:1, b:1}。还是那句话,少折腾就多获益。
不要对不怎么变化的值进行索引。比如性别,有无驾照这种布尔类型的量,对这个索引是没有任何意义的。排不出顺序,也就失去了索引的意义。
有些时候需要返回大量结果,比如返回的结果占到集的一般,这时候,最好就别用索引, 老老实实的用笨方法更快。因为当你要把一本书的前半本都读出来时,索引帮不上什么忙的,反而显得很这腾。所以不见得索引就一定快,要适合自己才是关键。

巧用oplog

by 10gen’s Mathias Stearn
翻译:程显峰

你已经使用了热备(还没有?),也已经知道设置起来非常容易。但可能你还不知道:所有的操作都会存储在一个常规的固定集中,在主控上是local.oplog.$main,在备份上是local.oplog.rs。通过查询这个集,可以深入探究系统的各个方面,甚至可以实现异步触发器。

?View Code JAVASCRIPT
> use local
switched to db local
> db.oplog.$main.find().sort({$natural:-1})
{ "ts" : { "t" : 1288884355000, "i" : 1 }, "op" : "u", "ns" :
"test.foo", "o2" : { "_id" : 1 }, "o" : { "$set" : { "i" : 2 } } }
{ "ts" : { "t" : 1288884349000, "i" : 1 }, "op" : "u", "ns" :
"test.foo", "o2" : { "_id" : 1 }, "o" : { "$set" : { "i" : 1 } } }
{ "ts" : { "t" : 1288884227000, "i" : 1 }, "op" : "i", "ns" :
"test.foo", "o" : { "_id" : 1, "some" : "thing" } }
{ "ts" : { "t" : 1288884225000, "i" : 1 }, "op" : "n", "ns" : "", "o" : { } }

可以看到有两次更新,一次插入,一次空操作(就是最下面的那个)。op可以是下列值:
i – insert
d – delete
u – update
c – command
n – no-op

空操作定期执行确保时效性。如果想过滤掉这些信息,在查询条件加入{op:{$ne:’n’}}就可以了。

很重要的一点就是所有oplog条目都是等幂的。例如上面的两次更新都是由{$inc:{i:1}}调用的,但是最后会转换成$set操作,这样就可以在以后多次重放。

MongoDB中的数据类型

JavaScript中的数只有一种类型64bit的浮点数。但是mongo中有三种分别是32bit的整数,64bit的整数,64bit的浮点数。

在mongo的shell中修改数字的话,很可能会改变数字的类型,这个大家要特别注意。不过估计很少有人会在shell直接修改数据吧。

还有一个问题是64bit整数保存成64bit浮点数,可能会有误差,这个请童鞋们仔细思考,慎重使用。想看看究竟的同学可以自己构造一个大数存进mongo中(当然一定不是在JavaScript中执行的,要不我不就白说了),然后看看结果。

mongo本身还提供其他有趣的类型,注意这些类型并不是所有编程语言都支持的。比如有些语言没有正则表达式类型,有些没有符号类型,有些语言没有日期类型。mongo还支持大部分语言都没有的最大最小类型。另外,你真的可以把JavaScript代码存在mongo里面,而且有一个对应的code类型。不过这么用的时候要慎重,当年blade爷就破口大骂将代码存在数据库里的愚蠢行为,不知道将来有一天他会不会这么做呢?

JavaScript支持的类型实在有限,所以在这里要警告大家,一定不要轻易在shell中修改数据回存。

MongoDB 东扯西

话说,传统的关系型数据库已经不那么吃香了。近来其他类型的数据库如雨后的春笋一般层出不穷,令人目不暇接,但也肯能非常令人恼火。究其原因,大多是觉得关系型数据库的性能不行,很多情况下也不适合互联网的应用。有针对性的,各路神侠构造了各种兵器,其中很大一类是基于key/value机制的数据库,典型的代表呢,就是Tokyo Cabinetmemcachedb。Tony Bain的一篇文章Is the Relational Database Doomed?对一些数据库发展的现状做了一个客观的评说。另外,还有ibm提出的xml的东西,不知道是个啥,半死不活的样子。很多年以前还有人搞了面向对象的数据库,后来也不怎么着,算是无疾而终(这么说很对不起还活着的兄弟,那能怎么样呢,不过是行尸走肉)。

无疑,数据库战场上的波澜会将每一个互联网开发者卷入其中。一波又一波的“去SQL化”浪潮(详见这里)在改变我们观念的同时,也在改变互联网的构建方式。

Document-Oriented database应运而生,典型代表

CouchDB 有篇不错的英文介绍

不过今天的主角是MongoDB注意啊不是MangoDB,让我们开开心心的开始吧。