【问题标题】:Mongodb: write results unavailable from shard01... caused by.. sharded connection pool: connect failedMongodb:从 shard01 写入结果不可用... 由.. 分片连接池:连接失败
【发布时间】:2014-09-26 15:33:14
【问题描述】:

环境

  1. mongo db 2.6.3
  2. CentOS 6.5 版(最终版)
  3. Java 1.7
  4. 雄猫 7
  5. Jmeter 2.11
  6. 亚马逊 ec2

我们的 mongo db 托管在 amazon ec2 中。 我们基于recommended production architecture设置了我们的服务器,如下:

  • 3 个配置服务器
  • 2个mongos和tomcats一起跑
  • 2个mongod,是一个主备副本(shard 1)的副本集

我们目前正在使用 3500 个并发用户对我们的应用程序进行负载测试。我们的应用程序消息传递(写入)繁重,因此我们目前正在试验 2 个数据库,一个用于用户,另一个用于消息。 当我们有单个数据库(用户,消息作为集合)时,平均响应时间为 2.3 秒,但错误率几乎为 0.00%。 当我们有 2 个 dbs 一个有用户,另一个有消息时,平均响应时间为 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}

~5.5% 的错误:

ReplicaSetMonitor no master found for set: shard01

~2.2% 的错误:

{ "serverUsed" : "localhost:27017" , "ok" : 1 , "n" : 0 , "err" : "could not contact primary for replica set shard01" , "code" : 7}

但是在抛出错误时,副本的主副本 (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
    }

连接池设置如下:

db.connections.max=5000
db.connections.min=5000

感谢任何有关修复错误的指针。

为回答马库斯而更新

你有两个成员的副本集?

是的,我们有一个 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) 

时间表?

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

【问题讨论】:

  • 几个问题:你有一个两个成员的副本集?您使用彩信监控吗?或者您至少可以为我们提供输出 sh.status() 吗?还请给出进行负载测试的准确时间范围以及发生此类错误的时间。
  • @MarkusWMahlberg 我已经通过更新问题回答了您的问题。如果上述信息不充分,我可以通过重新运行测试为您提供有关时间范围的更多信息。
  • 我需要确切的时间范围,即测试的开始时间和结束时间,以便将其与您的状态和选举相关联。

标签: java mongodb tomcat amazon-ec2 performance-testing


【解决方案1】:

先说几件事。

您的副本集设置

运行两个成员的副本集是一个非常糟糕的主意™。您至少应该有两个数据承载节点和一个正在运行的仲裁器。原因是选举总是需要多数票。因此,如果您的一个节点发生故障,则选举没有多数(两个节点之一不是多数),因此无法选举新的主节点。实际上,就可用性而言,您使情况变得更糟,因为您使一台机器发生故障并使集群进入无法写入的状态的可能性增加了一倍。请参阅 the introduction into replication in the MongoDB docs

你的分片键

你又做了一个非常糟糕的决定:使用 ObjectId 作为你的分片键。分片基于键范围。正如您在sh.status() 的输出中看到的那样,有一个从$minKey 到一个值的范围,一些范围具有值,最后但并非最不重要的一个范围是从一个值到$maxKey。由于 ObjectId 是单调递增的,因此所有新文档都将写入分配了此值的分片,而不是文档在分片中平均分配。平衡器解决这个问题,但是块迁移非常缓慢,并且您正在超载一个分片。这个问题还没有伤害到你,因为你只有一个分片,但是当你添加一个新分片时你会很快注意到。请深入了解 MongoDB 文档中的 Considerations for Selecting Shard Keys

你的尺码

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

发生了什么?

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

如何验证?

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

如何解决问题?

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

【讨论】:

  • 首先我们将添加仲裁器。当我使用 mongostat 进行监视时,那里要么有很多连接,要么有高数据传输,有很多插入,或者以上所有;每当错误率较高时就会发生。所以很明显,初选已经不堪重负了。
  • 我们的 shard key 不是 mongodb 默认的,而是自定义的,具有高基数。我们使用了自定义散列函数,它从我们拥有的 uuid 中产生 12 字节的散列。我们使用public ObjectId(byte[] b) 构造函数来生成它。
  • 我们使用了 5000 个连接,因为我们之前使用 5000 CCU 运行测试。现在我们将确保它与 tomcat(s) 中的线程数相同。
  • 不过,无论您使用什么来“加盐”ObjectID,它最后都会得到一个单调递增的计数器。如果您使用 db.yourColl.ensureIndex({_id:"hashed"}) 生成哈希索引并使用 `sh.shardCollection("yourDb.yourColl":{"_id":"hashed"}) 对集合进行分片,则不需要自定义哈希函数。关于连接:只需确保每个 tomcat 的池等于 maxThreads。否则:很高兴我能提供帮助。
  • 对不起,我的错。如果它适合你。 ;) 但是,使用副本集故障转移来处理负载峰值并不是一个好主意。副本集故障转移旨在提高发生硬件或网络中断时的可用性。将其视为您支付的保险,而不必为您的集群设置 24/7 支持。选举可能需要相当长的时间,但仍然比人工干预要快。如果您的分片无法承受负载,请添加另一个分片。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-02-11
  • 1970-01-01
  • 1970-01-01
  • 2011-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多