Mongodb:无法从 shard01 写入结果...引起的..分片连接池:连接失败

asp*_*pak 0 java tomcat amazon-ec2 performance-testing mongodb

环境

  1. 蒙戈数据库2.6.3
  2. CentOS 版本 6.5(最终版)
  3. 爪哇1.7
  4. 雄猫7
  5. Jmeter 2.11
  6. 亚马逊 EC2

我们的 mongo 数据库托管在亚马逊 EC2 中。我们根据推荐的生产架构设置了服务器,如下:

  • 3个配置服务器
  • 2 个 mongos 与 tomcats 一起运行
  • 2 mongod,这是一个具有主库和辅助库的副本集(分片 1)

我们目前正在对 3500 个并发用户的应用程序进行负载测试。我们的应用程序的消息传递(写入)量很大,因此我们目前正在尝试 2 个数据库,一个用于用户,另一个用于消息。当我们有单个数据库(用户、消息作为集合)时,平均响应时间为 2.3 秒,但错误率几乎为 0.00%。当我们有 2 个数据库时,其中一个包含用户,另一个包含消息,平均响应时间为 1.1 秒,但错误率更高 (0.16%)

当我们检查 tomcat(应用程序服务器日志)时,我们发现很多错误,如下所示:

~ 88% 的错误:

 { "serverUsed" : "localhost:27017" , "ok" : 1 , "n" : 0 , "err" : "write results unavailable from shard01-primary.mycompanys.com:27018 :: caused by :: Location13328 sharded connection pool: connect failed shard01-primary.mycompanys.com:27018 : couldn't connect to server shard01-primary.mycompanys.com:27018 (10.0.1.111), connection attempt failed" , "code" : 83}
Run Code Online (Sandbox Code Playgroud)

~5.5% 的错误:

ReplicaSetMonitor no master found for set: shard01
Run Code Online (Sandbox Code Playgroud)

~2.2% 的错误:

{ "serverUsed" : "localhost:27017" , "ok" : 1 , "n" : 0 , "err" : "could not contact primary for replica set shard01" , "code" : 7}
Run Code Online (Sandbox Code Playgroud)

但在引发错误时,副本的主副本 (shard01-primary.mycompanys.com) 正在运行。

shard01:PRIMARY> rs.status()
    {
        "set" : "shard01",
        "date" : ISODate("2014-08-04T08:57:59Z"),
        "myState" : 1,
        "members" : [
            {
                "_id" : 0,
                "name" : "shard01-primary.mycompanys.com:27018",
                "health" : 1,
                "state" : 1,
                "stateStr" : "PRIMARY",
                "uptime" : 1032189,
                "optime" : Timestamp(1406913104, 6),
                "optimeDate" : ISODate("2014-08-01T17:11:44Z"),
                "electionTime" : Timestamp(1406110686, 1),
                "electionDate" : ISODate("2014-07-23T10:18:06Z"),
                "self" : true
            },
            {
                "_id" : 1,
                "name" : "shard01-secondary.mycompanys.com:27018",
                "health" : 1,
                "state" : 2,
                "stateStr" : "SECONDARY",
                "uptime" : 1032005,
                "optime" : Timestamp(1406913104, 6),
                "optimeDate" : ISODate("2014-08-01T17:11:44Z"),
                "lastHeartbeat" : ISODate("2014-08-04T08:57:57Z"),
                "lastHeartbeatRecv" : ISODate("2014-08-04T08:57:57Z"),
                "pingMs" : 0,
                "syncingTo" : "shard01-primary.mycompanys.com:27018"
            }
        ],
        "ok" : 1
    }
Run Code Online (Sandbox Code Playgroud)

连接池设置如下:

db.connections.max=5000
db.connections.min=5000
Run Code Online (Sandbox Code Playgroud)

任何有关修复错误的指示都将受到赞赏。

更新回答马库斯

您有一个包含两个成员的副本集?

是的,我们有一个 2 成员副本集(主副本集、辅助副本集)。这是我们的 shard01。

您使用彩信监控吗?

是的,我们这样做。但我们可以为您提供 sh.status()

mongos> sh.status({verbose:true})
--- Sharding Status --- 
  sharding version: {
    "_id" : 1,
    "version" : 4,
    "minCompatibleVersion" : 4,
    "currentVersion" : 5,
    "clusterId" : ObjectId("53cf92e43476cd1989296134")
}
  shards:
    {  "_id" : "shard01-sh",  "host" : "shard01/shard01-primary.mycompanys.com:27018,shard01-secondary.mycompanys.com:27018" }
  databases:
    {  "_id" : "admin",  "partitioned" : false,  "primary" : "config" }
    {  "_id" : "my_app",  "partitioned" : false,  "primary" : "shard01-sh" }
    {  "_id" : "test",  "partitioned" : false,  "primary" : "shard01-sh" }
    {  "_id" : "my_app_load1",  "partitioned" : true,  "primary" : "shard01-sh" }
        my_app_load1.users
            shard key: { "_id" : 1 }
            chunks:
                shard01-sh  13
            { "_id" : { "$minKey" : 1 } } -->> { "_id" : ObjectId("119de91b3e18488b70e497a0") } on : shard01-sh Timestamp(1, 1) 
            { "_id" : ObjectId("119de91b3e18488b70e497a0") } -->> { "_id" : ObjectId("26b5524ea883044d602a56f0") } on : shard01-sh Timestamp(1, 17) 
            { "_id" : ObjectId("26b5524ea883044d602a56f0") } -->> { "_id" : ObjectId("3c2659b4eb7ae237566420e4") } on : shard01-sh Timestamp(1, 18) 
            { "_id" : ObjectId("3c2659b4eb7ae237566420e4") } -->> { "_id" : ObjectId("5b4be31feb7ae97c1e42e0e4") } on : shard01-sh Timestamp(1, 13) 
            { "_id" : ObjectId("5b4be31feb7ae97c1e42e0e4") } -->> { "_id" : ObjectId("6af6d205a883028e0c17d6f0") } on : shard01-sh Timestamp(1, 23) 
            { "_id" : ObjectId("6af6d205a883028e0c17d6f0") } -->> { "_id" : ObjectId("7c2752cbeb7aefbc1ff6f0e4") } on : shard01-sh Timestamp(1, 24) 
            { "_id" : ObjectId("7c2752cbeb7aefbc1ff6f0e4") } -->> { "_id" : ObjectId("954759cceb7aea12f666f0e4") } on : shard01-sh Timestamp(1, 15) 
            { "_id" : ObjectId("954759cceb7aea12f666f0e4") } -->> { "_id" : ObjectId("b1de2d00eb7ae972f93180e4") } on : shard01-sh Timestamp(1, 16) 
            { "_id" : ObjectId("b1de2d00eb7ae972f93180e4") } -->> { "_id" : ObjectId("c3d81bbca8830722302a5420") } on : shard01-sh Timestamp(1, 21) 
            { "_id" : ObjectId("c3d81bbca8830722302a5420") } -->> { "_id" : ObjectId("d642db1ac29660293b70e497") } on : shard01-sh Timestamp(1, 22) 
            { "_id" : ObjectId("d642db1ac29660293b70e497") } -->> { "_id" : ObjectId("e8afdf84a883072ba6e88420") } on : shard01-sh Timestamp(1, 19) 
            { "_id" : ObjectId("e8afdf84a883072ba6e88420") } -->> { "_id" : ObjectId("fd1771c93e1847d350e497a0") } on : shard01-sh Timestamp(1, 20) 
            { "_id" : ObjectId("fd1771c93e1847d350e497a0") } -->> { "_id" : { "$maxKey" : 1 } } on : shard01-sh Timestamp(1, 4) 
    {  "_id" : "my_app_inbox_load1",  "partitioned" : true,  "primary" : "shard01-sh" }
        my_app_inbox_load1.inbox
            shard key: { "receiver_id" : 1 }
            chunks:
                shard01-sh  20
            { "receiver_id" : { "$minKey" : 1 } } -->> { "receiver_id" : "0003fd94eb7aed675be420e4" } on : shard01-sh Timestamp(1, 17) 
            { "receiver_id" : "0003fd94eb7aed675be420e4" } -->> { "receiver_id" : "154b48b2eb7ae977588b70e4" } on : shard01-sh Timestamp(1, 19) 
            { "receiver_id" : "154b48b2eb7ae977588b70e4" } -->> { "receiver_id" : "26022e7eeb7aefb6ea5ac0e4" } on : shard01-sh Timestamp(1, 23) 
            { "receiver_id" : "26022e7eeb7aefb6ea5ac0e4" } -->> { "receiver_id" : "37f8d531c296675666f0e497" } on : shard01-sh Timestamp(1, 24) 
            { "receiver_id" : "37f8d531c296675666f0e497" } -->> { "receiver_id" : "41bcd983a883072cd2fc96f0" } on : shard01-sh Timestamp(1, 37) 
            { "receiver_id" : "41bcd983a883072cd2fc96f0" } -->> { "receiver_id" : "4cfd5606eb7aecd6ed2420e4" } on : shard01-sh Timestamp(1, 38) 
            { "receiver_id" : "4cfd5606eb7aecd6ed2420e4" } -->> { "receiver_id" : "622680c0eb7aecd6e88ac0e4" } on : shard01-sh Timestamp(1, 21) 
            { "receiver_id" : "622680c0eb7aecd6e88ac0e4" } -->> { "receiver_id" : "6df5ff8aeb7aea143936f0e4" } on : shard01-sh Timestamp(1, 25) 
            { "receiver_id" : "6df5ff8aeb7aea143936f0e4" } -->> { "receiver_id" : "80aabb00eb7ae237593590e4" } on : shard01-sh Timestamp(1, 26) 
            { "receiver_id" : "80aabb00eb7ae237593590e4" } -->> { "receiver_id" : "8ad740cbeb7aecddaff590e4" } on : shard01-sh Timestamp(1, 33) 
            { "receiver_id" : "8ad740cbeb7aecddaff590e4" } -->> { "receiver_id" : "95e04ae3eb7aecd58be6f0e4" } on : shard01-sh Timestamp(1, 34) 
            { "receiver_id" : "95e04ae3eb7aecd58be6f0e4" } -->> { "receiver_id" : "9fd32b25eb7aeba6ea5030e4" } on : shard01-sh Timestamp(1, 31) 
            { "receiver_id" : "9fd32b25eb7aeba6ea5030e4" } -->> { "receiver_id" : "b05d1766eb7aecd7588590e4" } on : shard01-sh Timestamp(1, 32) 
            { "receiver_id" : "b05d1766eb7aecd7588590e4" } -->> { "receiver_id" : "bab06fdfeb7ae8c587dac0e4" } on : shard01-sh Timestamp(1, 29) 
            { "receiver_id" : "bab06fdfeb7ae8c587dac0e4" } -->> { "receiver_id" : "c8dbfa5feb7aee075be590e4" } on : shard01-sh Timestamp(1, 30) 
            { "receiver_id" : "c8dbfa5feb7aee075be590e4" } -->> { "receiver_id" : "d4471acdeb7ae8c4388420e4" } on : shard01-sh Timestamp(1, 27) 
            { "receiver_id" : "d4471acdeb7ae8c4388420e4" } -->> { "receiver_id" : "e53cf32d3e184ff180e497a0" } on : shard01-sh Timestamp(1, 28) 
            { "receiver_id" : "e53cf32d3e184ff180e497a0" } -->> { "receiver_id" : "eecfd315a88305f2375ff6f0" } on : shard01-sh Timestamp(1, 35) 
            { "receiver_id" : "eecfd315a88305f2375ff6f0" } -->> { "receiver_id" : "ffd9ee77c296619a52e0e497" } on : shard01-sh Timestamp(1, 36) 
            { "receiver_id" : "ffd9ee77c296619a52e0e497" } -->> { "receiver_id" : { "$maxKey" : 1 } } on : shard01-sh Timestamp(1, 4) 
Run Code Online (Sandbox Code Playgroud)

大体时间?

它发生在测试运行中间的某个地方。我们运行了两次相同的测试,但它们产生了相同的错误率。我们刚刚清理了运行之间的数据,因此在第二次运行发生时分片和块已经存在(由第一次运行创建)。

Mar*_*erg 5

好的,首先有几件事。

\n

您的副本集设置

\n

运行两个成员副本集是一个非常糟糕的想法\xe2\x84\xa2。您至少应该有两个数据承载节点和一个正在运行的仲裁器。原因是选举总是需要多数票。因此,如果其中一个节点发生故障,则选举不会获得多数票(两个节点之一不是多数票),因此无法选出新的主节点。实际上,就可用性而言,您使情况变得更糟而不是更好,因为您使一台机器发生故障并将集群置于无法写入任何内容的状态的可能性增加了一倍。请参阅 MongoDB 文档中对复制的介绍

\n

你的分片键

\n

您做出了另一个非常错误的决定:使用 ObjectId 作为您的分片键。分片基于键范围。正如您在 的输出中看到的sh.status(),有一个从$minKey到 值的范围,一些范围包含值,最后但并非最不重要的一个范围是从值 到 的值$maxKey。由于ObjectId是单调递增的,所以所有新文档都将写入分配有该值的分片,而不是将文档平均分布在分片中。平衡器解决这个问题,但是块迁移非常慢,并且您正在使一个分片超载。这个问题还不会伤害您,因为您只有一个分片,但是当您添加新分片时您很快就会注意到。请深入了解MongoDB 文档中选择分片键的注意事项。

\n

您的尺码

\n

当持有 5000 个连接时,将使用大约 5GB 的物理 RAM,因为每个连接分配 1MB。根据您的硬件设置,当索引大小增加时您会遇到问题。当索引无法保存在 RAM 中时,MongoDB 的性能会急剧下降。我们在这里讨论的是几个数量级。您应该使用可重用连接的连接池,而不是使用如此大量的连接(我假设您想为每个并发用户分配一个连接)。关于大小:连接池应等于 Tomcat 安装的执行程序线程数。截至撰写本文时,默认大小为 200。由于一个线程无论如何都无法使用超过 1 个连接,因此可以肯定地说,每个 Tomcat 实例的连接池大小应为 200 (而不是该实例上运行的每个应用程序)。相应地调整您的值,因为您不太可能仅用 200 个线程来处理 3500 个用户。

\n

发生了什么?

\n

由于我没有您测试的确切时间范围,因此我只能对发生的情况进行有根据的猜测。最有可能的是,您的副本集成员太小,并且您的主副本集超载,以至于它无法回复辅助副本心跳请求。在这种情况下,辅助节点会在一段时间后宣布主节点失效,并尝试发起选举。这次选举以平局结束(两个中的一个不是多数),所以次要的仍然是次要的。但由于 MongoDB 驱动程序和分片集群都知道副本集,因此两者都会知道进行了一次选举,并且根据平局,在他所知道的上次选举期间,没有为副本集选举出主节点。初选仍在运行并不重要,因为它很可能已经超载而无法参加选举(因为超载而没有回应心跳是选举最初举行的原因)。

\n

需要做什么来验证?

\n
    \n
  1. 查看辅助服务器的日志文件,并尝试找出在负载测试期间是否进行了选举。
  2. \n
  3. 深入研究主节点的 MMS 统计信息。我强烈假设您会在测试期间看到高锁定百分比以及与操作计数器数量相关的大量页面错误
  4. \n
  5. 尝试找出您的负载测试是否围绕 ISODate("2014-07-23T10:18:06Z") 运行。这更像是一种可能,但值得一试。
  6. \n
\n

该怎么做才能解决问题?

\n
    \n
  1. 阅读我上面的笔记,包括链接的文档
  2. \n
  3. 冲洗并重复;)
  4. \n
  5. 至少运行一个仲裁器。我通常不建议这样做,因为当您必须关闭承载数据的副本集成员之一以进行维护或备份时,您就会失去故障转移功能。在分片环境中,我始终建议为副本集部署至少 3 个数据承载节点和 2 个仲裁器,以防一个数据承载节点在另一数据承载节点上进行维护操作期间意外停机仲裁器在资源方面非常便宜,并且在配置服务器上运行它们是可以的。只要始终确保您有奇数个投票节点即可。
  6. \n
  7. 选择合适的分片键。
  8. \n
  9. 添加分片或扩展现有分片。CPU 并不是什么大问题,RAM 和大容量存储延迟才是问题。
  10. \n
\n