AnyMongo – MongoDB driver for AnyEvent application
最近做了一个mongoDB在AnyEvent上驱动原型,大部分代码来自官方的perl driver,部分则来自ruby driver。我觉得ruby driver
比较有意思。 替换了普通版本的sock相关的操作,使用AE的handler替代,增加了BSON的decode和encode,重写了write_***函数。
同时,提供了一个AnyMongo::Compat兼容包, 原先使用MongoDB驱动的代码应该可以直接运行,因为这个包可以通过官方0.36的
大部分测试 t/perl-driver-api/*t . 对于使用了一些底层函数比如write/recv的代码则无法兼容。
由于是prototype,所以目前版本谨供学习和参考,不可能在正式使用,因为还缺少很多功能,包括authentication, paris/replica-set
支持,reconnect, timeout 等等。
性能上也不好不坏, 对于crud操作,要比官方的快20-30%, 但对于cursor操作,则相反要慢30%1%左右。官方版本的cursor的性能也
还是有点问题,因为当cursor遍历的记录数在10万以上时,双方的差距缩小到5%1%左右。
anymongo性能提升的空间还是很大,目前还是先把缺少的功能补上,同时也要测试Coro的兼容性,
最后再考虑解决cursor的性能问题。
计划在0.10左右可以投入生产使用,正好可以用在我们新项目上。
AnyMongo使用Perl license,源码:
http://github.com/nightsailer/any-mongo
Updated:
简单做了profiling,发现瓶颈在于一些调试代码和moose。去除调试代码,启用moose inlined destruction,
现在anymongo的cursor和官方版本几乎一样了,只有不到1%的差距。
MongoX v0.002 release
新增:
- with_context
with_context {
#your code
} db => ‘db1′
with_context可以创建一个context沙箱并运行代码block。 沙箱的概念是block中对于context_db/connection/collection的改变不会影响到代码外。这是对context对象local化了。
- for_connections
- for_dbs
- for_collections
在不同的dbs,collections,connections上循环执行代码段。当代码需要在不同的数据库和collection中执行时有用。
MongoDB commands的封装:
封装了以下commands :
普通的commands:
db_list_commands
db_stats
db_is_master
db_eval
db_add_user
db_remove_user
db_auth
db_create_collection
db_convert_to_capped
db_ping
db_repair_database
db_run_command
db_current_op
db_re_index
db_filemd5
db_map_reduce
db_distinct
db_group
db_insert
db_count
db_remove
db_update
db_update_set
db_find_one
db_find
db_find_all
db_find_and_modify
db_increment
这些是用于admin的commands:
admin_fsync_lock
admin_unlock
admin_server_status
admin_shutdown_server
admin_build_info
admin_get_cmd_line_opts
admin_log_rotate
admin_logout
admin_resync
admin_sharding_state
admin_unset_sharding
admin_diag_logging
下载:
http://github.com/nightsailer/mongo-x/tarball/v0.002
CPAN:
http://search.cpan.org/~doggy/MongoX-0.002/
I’m back,Perl!
在相当长的一段时间,我的首选开发语言是PHP,开发框架是Doggy,从PHP3 到 PHP 5.2.11,基本完备。
混合模式下,曾经使用过的其他的开发语言包括Perl, Java, C, C++。
PHP 是不错的Web开发的工具,但目前我彻底有些厌倦,尤其是对5.3系列,非常失望。和Ruby和Python不一样,
现在从PHP体会不到一丝新意,也感受不到乐趣。 Ruby 和 Python 虽然一直默默关注,但从未实践过,缺乏痛苦
的实践,就没有发言权。
Perl我一直作为后端处理的工具,这些年也从未丢过。CPAN给我提供了无限可能的资源,尽管有时候筛选可用的
模块也是比较麻烦的事情。Moose、Plack、Daner 这些让我看到Perl敏捷开发的新意。虽然Perl6还遥不可及,
但Perl5的开发终于重新启动,这让我有些兴奋。所以,今后一段时间,我会更关注Perl。
在目前计划的产品中,仍然使用MongoDB作为标配,Perl则不仅仅用于后台处理,将尝试进入前端,
Daisy 将是我近期开发的重点,这是基于Plack, Moose, MongoDB,类似Dancer的轻量级web framework。
和Doggy的封闭不同,Daisy将使用Perl许可进行开发,我会放到github上。
除了Perl, Object-C也将是我的兴趣点。iPad app是年内计划完成的事。
MongoX 0.001
发布了 MongoX-0.001, 封装了几个简单的syntax。
懒惰是程序员的美德,MongoX 就是解决我犯懒的小东西。
经常在用Perl做一些小工具进行MongoDB的维护。我希望能够更加简单清爽的使用,就像在mongo shell
里操作那样简单。于是,就封装了几个简单的类似DSL的语法。
# 最快速模式,连接到127.0.0.1,默认数据库 test
use MongoX host => ‘mongodb://127.0.0.1′,db => ‘test’;
# 按部就班的来
use MongoX;
#添加一个默认的connection
add_connection host => ‘127.0.0.1′;
#启用默认的connection
use_connection;
#切换到数据库 test
use_db ‘test’;
#添加另一个数据库连接,连接id为remote2
add_connection host => ‘192.168.1.1′,id => ‘remote2′;
#切换到remote2 连接
use_connection ‘remote2′;
#返回当前数据库中 foo这个collection
my $foo = get_collection ‘foo’;
#将 ‘foo’ 设置为默认的collection
use_collection ‘foo’;
#context_collection是默认collection
say ‘total rows:’,context_collection->count();
my $id = context_collection->insert({ name => ‘Pan’, home => ‘Beijing’ });
# context_db是当前默认的数据库,即 remote2连接中的foo
my $gridfs = context_db->get_gridfs;
当前就这些,哪天犯懒再把常用的mongo shell的一些helper整进来。
MongoX 目前已经上传到CPAN,可以
cpanm MongoX
就可以使用了。
源码库在 Github:
http://github.com/nightsailer/mongo-x
欢迎fork!
不同数据中心同步处理
很久之前看过facebook关于不同数据中心之间的延迟处理。
目前我们也遇到类似的问题。这次我们使用的是mongodb。
由于国内恶劣的环境和昂贵的价格,我们被迫将一些大量消耗带宽的服务迁移到国外的主机。这导致了大量的网络延迟。
大概200-400ms。 这样,仅仅依靠mongodb的同步是无法解决数据的一致性了。
由于数据同步量很大,又通过VPN,主数据中心和国外同步的时间周期要以分钟甚至小时计算了。
没办法,只能土法了。
当前解决方案是:
1. 数据一律从国内dc进行更新,此时设置更新数据状态为未同步
2. 数据更新后,发送一个消息到国外dc的队列,对应的worker则开始监控此数据的同步情况
3. 数据未同步前,所有的数据均从国内的主数据进行分发处理
4. 同步完毕,国外dc通知国内dc,将数据状态更改为已同步
5. 一旦同步,后续的数据处理工作自动转发到国外,从而实现分流
缺点:
监控worker和队列的工作状态需要监控。否则会导致流量无法分发。
虽然是土法,但也能解决眼前的问题。
6月小结:世界杯
四年一度的世界杯马上要曲终人散了。生活又要归于平淡的忙碌。
6月份几乎没有写什么东西,连推也很少上。最大的乐趣是每天和同事们下注赌球,然后半夜一起看球。
有幸被忽悠成了公司世界杯观球会的首个冠名赞助商,代价是1箱青岛啤酒,回报是,每次进入会议室,
可以有掌声鼓励,可以坐投影下的最前排,呵呵,和同事一起开投影看球,当然少不了赌注,嗯,在公司的
几次都赢了。
让我悲情的是阿根廷对德国,虽然从投机角度,我押了赔率最高的德国赢,但仍希望是阿根廷出现,老马再
得意一场,最终的结果,钱赢了,心却不忍。
技术方面,这个月个人关注的是Perl方面的东西,还有几个microframework,如Dancer,Sinatra 之类的。正写一个自己的micro framework, 希望能介于dancer和catalyst之间。国内目前用Perl的已经绝迹了,但我仍然力推
Perl作为我们未来产品的首选开发语言,这就是因地制宜了。
年初兼任了社区产品的产品经理,目前正在做社区产品的改版。希望这次能够有些进步。做产品和做技术研究,以及做项目差别很大,
杯具的是,我现在需要把自己的精力在三者进行协调。
我是一个典型的宅人,这次公司的海边游玩,争取到最后终于可以不去,安静在家里看看书,写写code,这些都是平日无法去作的,乐得其所,真是畅快呀!
骄阳似火,7月注定又是忙碌的一个月,希望当世界杯激情消退后,这些看似简单而令人乏味,枯燥但颇具挑战的工作能带来同样的快乐。
“get File(): bad file number value (corrupt db?): run repair”
好好的端午节,看球看的昏天黑地,押注正欢之际,机房来了个电话,告知,由于空开跳闸,我们的半个机柜歇菜了,有台r710还把电源线烧了。 查了一下,杯具啊, 主要的mysql 集群和mongodb 竟然都在这半个机柜中。
没办法,在乌乌祖啦的嗡嗡中,开始恢复数据。
Mysql 是轻车熟路了,直接把主从数据手动复制后重新启动mmm就好了。
MongoDB大部分都正常,不过有个节点出了点小差错,在validate的时候报错:
get File(): bad file number value (corrupt db?): run repair
嗯,看来也需要修复数据库了。
注意:
如果有类似现象的朋友,一定要把节点的local/admin数据库也repair. local用于master.
然后,把slave节点重新resync一下。
Non-blocking/Evented/IO 将会改变我们传统的编程模式
最近Node.js 很火, 我对此很有兴趣,我自己也在使用Perl的AnyEvent。
Event/IO的一个要点是non-blocking。 Nginx能够解决c10k的问题,一个关键是使用
Event Loop取代传统的thread/pre-fork。要想充分发挥nginx的优势,需要后端和对应
模块都能够non-blocking才行。
然而,现实多数的架构都是blocking的,比如PHP/FastCGI, 即便nginx的前端再好,后端的应用
依然是瓶颈。
所以,如何使用Event/IO实现non-blocking编程,是我们要关注的一个领域。
Ruby的EventMachine, Python的Twisted, Perl的AnyEvent都是不错的开始。
Node.js则是另一个选择。 Javascript似乎是天生的适合作Event/IO的编程语言。 上手轻松,简单。
对于我们来说,最困难的是开始,如何适应新的基于callback的处理模式,这是最大的挑战。
在未来一段时间内,我将把注意力集中到node.js和AnyEvent. 对于我目前正在规划的视觉中国C10平台中,这将是一个非常有趣解决方案。
推荐关于node.js的slide:
http://nodejs.org/cinco_de_node.pdf
Why You Should Pay Attention to Node.Js
MongoDB的备份方式
简要说一下MongoDB的备份方式:
1. mogodump / mongorestore
这2命令将mongodb的数据dump为BSON格式,需要的时候可以恢复。
这种方式作为小的数据库还适用。但如果是sharding或者几百G数据以上的话
就几乎不可用了。因为BSON及其占用空间。
2. Slave Replication
这是最适合和可靠的,适合生产环境。MongoDb支持master+多个slave。因此可以很方便的起一个slave来进行备份。
但是需要注意的是如果数据量很大的话,如果是新建的slave,速度又没有保证的话,一定要调高 oplogSize 的大小,对于一个300G的数据库,
可以调到60G以上。这样避免同步到中间出现oplogSize不足等异常。
3. 关于增量热备份
对于某些热衷单独文件备份的同志,也可以考虑使用“增量热”备份的方式作为最后的屏障。
做法就是,最开始将某个slave暂时shutdown,然后将数据库文件复制到另一个目录作为增量备份的起点。
每次备份的时候,在这个目录上起一个slave, 使用–fastsync参数,同步完毕即可tar这个目录就可以了。
这种方式的一个缺点就是如果备份周期的过长的话,空间浪费会非常大,尤其是频繁删除的情况下,
目前1.6以前的版本对于删除的空间回收机制有bug。这个问题应该在1.6版本发布时候解决。
要想回收这些空间需要repairDatabase,速度比较慢,不如直接从头clone一个新的db了。(其实Repair的过程也就是先clone然后copy回来)。
综上所述, 最靠谱和安全的备份方式就是用mongodb自身的同步复制机制.
最经典也最小的生产环境是:
1 master + 2 slaves
Gearman::Driver是不错的Geamman worker管理器
最近发现了Gearman::Driver。可以方便的编写gearman的worker。由于使用了Moose和POE,扩展起来相当简便。比如可以对某个class的method
添加一个Job attribute,就能够实现worker了:
package # hide from PAUSE
Gearman::Driver::Test::Live::RuntimeOption;
use base qw(Gearman::Driver::Test::Base::All);
use Moose;
sub job1 : Job :MinProcesses(2): MaxProcesses(10) {
my ( $self, $job, $workload ) = @_;
return $self->foo;
}
1;
job1就自动注册为:’ Gearman::Driver::Test::Live::RuntimeOption::job1′ 了。MinProcesses, MaxProcesses则定义worker要spawn的进程数。
Gearman::Driver还实现了一个console,通过telnet可以动态的更改worker的进程数,非常的方便。
美中不足的是,Gearman::Driver的worker 无法通过配置文件来配置,在部署的时候很不方便。 对于一些运行时要读取某些配置的worker,
只能通过hard code或者$ENV来传递。
于是,我fork后做了写改进,添加了一些选项,启动时使用–configfile就可以从对应的配置文件读取了。