MongoDB并发瓶颈

时间:2015-04-07 14:21:05

标签: mongodb concurrency

太长;没有读

问题是关于我在MongoDB上遇到的并发瓶颈。如果我进行一次查询,则需要1个单位的时间才能返回;如果我进行2个并发查询,则需要2个单位的时间才能返回;通常,如果我进行n个并发查询,则所有这些查询都需要n个单位才能返回。我的问题是如何在面对并发查询时改善Mongo的响应时间。

设置

我在运行MongoDB 2.6.7服务器的AWS上有一个m3.medium实例。 m3.medium有1个vCPU(Xeon E5-2670 v2的1个核心),3.75GB和4GB SSD。

我有一个名为user_products的单个集合的数据库。此集合中的文档具有以下结构:

{ user: <int>, product: <int> }

有1000个用户和1000个产品,每个用户 - 产品对都有一个文档,总计一百万个文档。

该集合的索引为{ user: 1, product: 1 },我的结果均为 indexOnly

测试

测试是在运行MongoDB的同一台机器上执行的。我正在使用Mongo提供的benchRun函数。在测试期间,没有其他对MongoDB的访问,测试只包含读操作。

对于每个测试,模拟了许多并发客户端,每个客户端都会尽可能多地进行一次查询,直到测试结束。每次测试运行10秒。并发性以2的幂为单位进行测试,从1到128个并发客户端进行测试。

运行测试的命令:

mongo bench.js

这里是完整的脚本(bench.js):

var
    seconds = 10,
    limit = 1000,
    USER_COUNT = 1000,
    concurrency,
    savedTime,
    res,
    timediff,
    ops,
    results,
    docsPerSecond,
    latencyRatio,
    currentLatency,
    previousLatency;

ops = [
    {
        op : "find" ,
        ns : "test_user_products.user_products" ,
        query : {
            user : { "#RAND_INT" : [ 0 , USER_COUNT - 1 ] }
        },
        limit: limit,
        fields: { _id: 0, user: 1, product: 1 }
    }
];

for (concurrency = 1; concurrency <= 128; concurrency *= 2) {

    savedTime = new Date();

    res = benchRun({
        parallel: concurrency,
        host: "localhost",
        seconds: seconds,
        ops: ops
    });

    timediff = new Date() - savedTime;

    docsPerSecond = res.query * limit;
    currentLatency = res.queryLatencyAverageMicros / 1000;

    if (previousLatency) {
        latencyRatio = currentLatency / previousLatency;
    }

    results = [
        savedTime.getFullYear() + '-' + (savedTime.getMonth() + 1).toFixed(2) + '-' + savedTime.getDate().toFixed(2),
        savedTime.getHours().toFixed(2) + ':' + savedTime.getMinutes().toFixed(2),
        concurrency,
        res.query,
        currentLatency,
        timediff / 1000,
        seconds,
        docsPerSecond,
        latencyRatio
    ];

    previousLatency = currentLatency;

    print(results.join('\t'));
}

结果

结果总是这样(为了便于理解,省略了输出的某些列):

concurrency  queries/sec  avg latency (ms)  latency ratio
1            459.6        2.153609008       -
2            460.4        4.319577324       2.005738882
4            457.7        8.670418178       2.007237636
8            455.3        17.4266174        2.00989353
16           450.6        35.55693474       2.040380754
32           429          74.50149883       2.09527338
64           419.2        153.7325095       2.063482104
128          403.1        325.2151235       2.115460969

如果只有一个客户端处于活动状态,则它可以在10秒测试中每秒执行大约460次查询。查询的平均响应时间约为2毫秒。

当两个客户端同时发送查询时,查询吞吐量维持在每秒约460个查询,表明Mongo没有增加其响应吞吐量。另一方面,平均潜伏期实际上翻了一番。

对于4个客户,模式继续。相同的查询吞吐量,相对于运行的2个客户端,平均延迟加倍。列latency ratio是当前和之前测试的平均延迟之间的比率。看到它总是显示延迟加倍。

更新:更多CPU功率

我决定使用不同的实例类型进行测试,改变vCPU的数量和可用RAM的数量。目的是了解添加更多CPU功率时会发生什么。测试的实例类型:

Type        vCPUs  RAM(GB)
m3.medium   1      3.75
m3.large    2      7.5
m3.xlarge   4      15
m3.2xlarge  8      30

结果如下:

Queries per Second

Query Latency

m3.medium

concurrency  queries/sec  avg latency (ms)  latency ratio
1            459.6        2.153609008       -
2            460.4        4.319577324       2.005738882
4            457.7        8.670418178       2.007237636
8            455.3        17.4266174        2.00989353
16           450.6        35.55693474       2.040380754
32           429          74.50149883       2.09527338
64           419.2        153.7325095       2.063482104
128          403.1        325.2151235       2.115460969

m3.large

concurrency  queries/sec  avg latency (ms)  latency ratio
1            855.5        1.15582069        -
2            947          2.093453854       1.811227185
4            961          4.13864589        1.976946318
8            958.5        8.306435055       2.007041742
16           954.8        16.72530889       2.013536347
32           936.3        34.17121062       2.043083977
64           927.9        69.09198599       2.021935563
128          896.2        143.3052382       2.074122435

m3.xlarge

concurrency  queries/sec  avg latency (ms)  latency ratio
1            807.5        1.226082735       -
2            1529.9       1.294211452       1.055566166
4            1810.5       2.191730848       1.693487447
8            1816.5       4.368602642       1.993220402
16           1805.3       8.791969257       2.01253581
32           1770         17.97939718       2.044979532
64           1759.2       36.2891598        2.018374668
128          1720.7       74.56586511       2.054769676

m3.2xlarge

concurrency  queries/sec  avg latency (ms)  latency ratio
1            836.6        1.185045183       -
2            1585.3       1.250742872       1.055438974
4            2786.4       1.422254414       1.13712774
8            3524.3       2.250554777       1.58238551
16           3536.1       4.489283844       1.994745425
32           3490.7       9.121144097       2.031759277
64           3527         18.14225682       1.989033023
128          3492.9       36.9044113        2.034168718

xlarge 类型开始,我们开始看到它最终处理2个并发查询,同时保持查询延迟几乎相同(1.29毫秒)。但是,它不会持续太长时间,并且对于4个客户端,它再次使平均延迟时间加倍。

使用 2xlarge 类型,Mongo能够处理多达4个并发客户端,而不会过多地增加平均延迟。之后,它又开始翻倍。

问题是:可以采取哪些措施来改善Mongo在进行并发查询方面的响应时间?我希望看到查询吞吐量有所增加而且我做了< em> not 期望看到它将平均延迟加倍。它清楚地表明Mongo无法并行化到达的查询。

在某些地方存在限制Mongo的某种瓶颈,但它确实无助于增加更多的CPU功率,因为​​成本太高了。我不认为内存是一个问题,因为我的整个测试数据库很容易适应RAM。还有什么我可以尝试的吗?

1 个答案:

答案 0 :(得分:0)

您正在使用1核心的服务器并且您正在使用benchRun。来自benchRun page

  

此benchRun命令被设计为QA基线性能测量工具;它并非旨在成为&#34;基准&#34;。

使用并发数字缩放延迟非常准确。你确定计算是否正确?我可以相信ops / sec / runner保持不变,延迟/操作也保持不变,因为跑步者的数量增长了 - 然后如果你添加了所有延迟,你会看到像你的结果。