MongoDB的JavaScript性能

两个多月前,我与下文作者dulao5兄有过一次关于MongoDB JavaScript执行效率的聊天,昨天dulao5完成此文,将链接发给我,我看完后对测试结果很是震惊,下面是原文,欢迎对MongoDB JavaScript引擎有研究的同学共同探讨。

原文:www.dulao5.com

mongodb的db.eval

mongodb使用javascript做shell, mongodb的db.eval可以提供给数据驱动与这种javascript shell类似的js接口. 这算是一种移动代码风格(Mobile Code Sytles)的架构设计吧. 我期望可以利用这个接口, 将一些处理逻辑放在mongodb的节点上运行. 避免在PHP里面多次获取和操作mongodb的数据. (这个需求类似传统数据库的存储过程)

但是测试却发现, 在mongodb里面跑javascript性能很不理想. 我写了一段测试代码, 跑10000次循环:

<?php
$mongo = new Mongo("mongodb://localhost:20237/");
$db = $mongo->selectDB("attl");

$echo_func = <<<JAVASCRIPT
function() {
        var str = "xxxxxxxxxxxxxxxxx0000000000";
        var data = str + str + str + str;
        var data = data + data + data + data;
        var max = 10000;
        var arr = [];
        var total = 0;

        for(var a=0; a<100; a++) {
                for(var i=0; i<max; i++) {
                        arr.push( data + " . " + data);
                }
                for(var i=0; i<arr.length; i++) {
                        total += arr[i].length;
                }
                arr = [];
        }
        return total;
}
JAVASCRIPT;

$bgtime = microtime(true);

$echo_code = new MongoCode($echo_func);
$obj = (object)array("name"=>"dzg", "values"=>array(1,2,3), "bool"=>true) ;
$arr = array(1,2,3,4,5,6,7,8);
$map = array("a"=>1, "b"=>2);
$response = $db->execute($echo_code, array($obj, $arr, $map));
var_dump($response);

$endtime = microtime(true);

echo "\n time : ".($endtime - $bgtime)."\n";

执行 :

php ab-mongo.php
array(2) {
  ["retval"]=>  float(867000000)
  ["ok"]=>  float(1)
}

time : 6.0353651046753

这段js居然执行6秒之久! 在其他环境下测试同样功能代码:

  • Google Chrome : 只需70毫秒
  • Firefox 5 : 只需180毫秒
  • 用PHP 5执行类似代码: 只需630毫秒

显然相比以上测试, mongodb的javascript性能差太大了.

排除测试干扰

根据mongodb官方文档 Server-side Code Execution : Limitations of eval > Write locks 一节, 我怀疑也许是Write locks导致的. 但这种怀疑和本例关系不大, locks应该只影响并发执行, 现在的问题是一次执行都这么慢.

另外, 我还担心从PHP到mongodb的js代码传递环节慢.

于是花了两种手段排除这两个因素:

使用 db.system.js.save({_id: “mytestfunc”, value: function () { … }}); 的方式, 将测试的js代码作成mongodb支持的Stored JavaScript

使用 db.runCommand({$eval: function() {return mytestfunc();}, nolock: true}) 的方式, 进行nolock调用

结果发现问题依旧. 结论是mongodb的JavaScript执行环节有问题

测试Mongodb + V8 JavaScript引擎

我知道Mongodb的JavaScript引擎是SpiderMonkey, 于是想尝试再编译一个v8版本的Mongodb, 准备用如下JavaScript文件测试一下.

准备测试代码

function dotest() {
        var str = "xxxxxxxxxxxxxxxxx0000000000";
        var data = str + str + str + str;
        var data = data + data + data + data;
        var max = 10000;
        var arr = [];
        var total = 0;

        for(var a=0; a<100; a++) {
                for(var i=0; i<max; i++) {
                        arr.push( data + " . " + data);
                }
                for(var i=0; i<arr.length; i++) {
                        total += arr[i].length;
                }
                arr = [];
        }
        return total;
}

myecho = (typeof console !== 'undefined' && typeof console.log == 'function') ? console.log : print;

a = new Date();
myecho("begin:\t" + a);

myecho("result:\t" + dotest());

b = new Date();
myecho("end:\t" + b);

myecho("total time:\t" + (b - a));

这个JavaScript脚本能同时跑在4种环境下:

  • Chrome / Firefox等浏览器环境
  • node js的shell
  • SpiderMonkey 的js shell
  • mongo 的js shell

我准备编译完Mongodb的v8版本后, 四个环境都比较测试一下.

编译mongodb + v8

我在OpenSuse11.4下, 用linux的包管理工具准备编译环境, 还是很容易的:

#已安装的包: g++ subversion git python

sudo zypper install scons tcsh boost-devel pcre-devel readline-devel

svn checkout http://v8.googlecode.com/svn/trunk/ v8
cd v8
scons
cd ..

git clone git://github.com/mongodb/mongo.git
cd mongov8
scons

#run mongodb v8
./mongod --quiet --shardsvr --dbpath /home/dzg/data/mongodb/test/mongod --port 20237 --nohttpinterface --fork --pidfilepath /home/dzg/data/mongodb/test/mongod.pid --logpath /home/dzg/log/mongodb/test/mongod.log --logappend

执行测试

执行~/opt/mongov8/mongo localhost:20237 dotest.js

  • mongo+v8 : 93毫秒
  • nodejs : 68毫秒
  • SpiderMonkey : 442毫秒

初步结论:

  • mongodb + v8性能非常好, 接近node.js里面的执行性能
  • SpiderMonkey 1.9 比v8慢5倍
  • mongodb使用SpiderMonkey肯定有一些环节还存在问题, 导致mongodb+SpiderMonkey比mongodb+v8慢60多倍
    • 也许是mongodb的SpiderMonkey版本低? 我目前还不知道mongodb的发行版内, SpiderMonkey的确切版本
    • 我上面测试SpiderMonkey 的js命令是来自xulrunner-192
  • 测试代码很短, 比较片面, 上面的性能倍数只是大体估计, 不是全面精确的评估

anyShare据说看到好文章不转的人,服务器容易宕机!
          

无觅相关文章插件,快速提升流量

  1. 兄弟你的测试还是有问题的。我建议你直接跑一下以下代码:
    > arr = []
    > for(var i=0;i<1000000;i++)arr.push(i)

    同样的100w次,你可以感受一下时间,相信几秒和几毫秒还是很容易区分的

  2. 另外,对于JS的弱类型,如果单纯的赋值是可以的,但如果是不申请类型就直接调用像push这种方法,是肯定会报错退出的,这就是之前时间非常短的原因。

  3. [root@localhost bin]# ./mongo 10.10.21.163 var startTime = new Date();
    set162163228:SECONDARY> var arr =[]
    set162163228:SECONDARY>
    set162163228:SECONDARY> for(var i=0; i
    set162163228:SECONDARY> (new Date().getTime()-startTime.getTime())/10000
    0.2695
    set162163228:SECONDARY> bye

  4. (new Date().getTime()-startTime.getTime())/10000
    这里应该除1000

    你测试时应该感觉到了两秒多,不应该相信打出来的一个数字啊。