MongoDB并发瓶颈

Luc*_*iva 7 concurrency mongodb

太长; 没看过

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

安装程序

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

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

{ user: <int>, product: <int> }
Run Code Online (Sandbox Code Playgroud)

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

该集合有一个索引{ user: 1, product: 1 },我的结果都是indexOnly.

考试

测试是在运行MongoDB的同一台机器上执行的.我正在使用benchRunMongo提供的功能.在测试期间,没有对MongoDB进行其他访问,并且测试仅包含读取操作.

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

运行测试的命令:

mongo bench.js
Run Code Online (Sandbox Code Playgroud)

这是完整的脚本(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'));
}
Run Code Online (Sandbox Code Playgroud)

结果

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

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
Run Code Online (Sandbox Code Playgroud)

如果只有一个客户端处于活动状态,则它可以在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
Run Code Online (Sandbox Code Playgroud)

结果如下:

每秒查询数

查询延迟

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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

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

问题是:在进行并发查询方面,可以采取哪些措施来改善Mongo的响应时间?我期望看到在查询吞吐量的增长,我并没有期望看到它加倍平均延迟.它清楚地表明Mongo无法并行化到达的查询.

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

wdb*_*ley 0

您正在使用具有 1 个核心的服务器,并且您正在使用 benchRun。从benchRun 页面

该 benchRun 命令被设计为 QA 基线性能测量工具;它并不是旨在成为“基准”。

延迟与并发数的比例是否精确得令人怀疑。你确定计算正确吗?我可以相信,随着运行者数量的增加,操作/秒/运行者保持不变,延迟/操作也保持不变 - 然后,如果您添加所有延迟,您会看到像您这样的结果。