问题是关于我在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
是当前和之前测试的平均延迟之间的比率。看到它总是显示延迟加倍。
我决定使用不同的实例类型进行测试,改变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
结果如下:
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。还有什么我可以尝试的吗?
答案 0 :(得分:0)
您正在使用1核心的服务器并且您正在使用benchRun。来自benchRun page:
此benchRun命令被设计为QA基线性能测量工具;它并非旨在成为&#34;基准&#34;。
使用并发数字缩放延迟非常准确。你确定计算是否正确?我可以相信ops / sec / runner保持不变,延迟/操作也保持不变,因为跑步者的数量增长了 - 然后如果你添加了所有延迟,你会看到像你的结果。