MongoDB 日常運維實踐總結(jié)

一、MongoDB 集群簡介

MongoDB是一個基于分布式文件存儲的數(shù)據(jù)庫蔗喂,其目的在于為WEB應(yīng)用提供可擴展的高性能數(shù)據(jù)存儲解決方案耕姊。下面將以3臺機器介紹最常見的集群方案蚓耽。具體介紹,可以查看官網(wǎng) https://docs.mongodb.com/v3.4/introduction/频祝。

1泌参、集群組件的介紹

  • mongos(路由處理):作為Client與MongoDB集群的請求入口,所有用戶請求都會透過Mongos協(xié)調(diào)常空,它會將數(shù)據(jù)請求發(fā)到對應(yīng)的Shard(mongod)服務(wù)器上沽一,再將數(shù)據(jù)合并后回傳給用戶。

  • config server(配置節(jié)點):即:配置服務(wù)器漓糙;主要保存數(shù)據(jù)庫的元數(shù)據(jù)铣缠,包含數(shù)據(jù)的分布(分片)以及數(shù)據(jù)結(jié)構(gòu),mongos收到client發(fā)出的需求后昆禽,會從config server加載配置信息并緩存于內(nèi)存中蝗蛙。一般在生產(chǎn)環(huán)境會配置不只一臺config server,因為它保存的元數(shù)據(jù)極為重要醉鳖,若損壞則影響整個集群運作捡硅。

  • shard(分片實例存儲數(shù)據(jù)):shard就是分片。MongoDB利用分片的機制來實現(xiàn)數(shù)據(jù)分布存儲與處理盗棵,達到橫向擴容的目的壮韭。默認情況下,數(shù)據(jù)在分片之間會自動進行移轉(zhuǎn)纹因,以達到平衡喷屋,此動作是靠一個叫平衡器(balancer)的機制達成。

  • replica set(副本集):副本集實現(xiàn)了數(shù)據(jù)庫高可用辐怕,若沒做副本集逼蒙,則一旦存放數(shù)據(jù)的服務(wù)器節(jié)點掛掉,數(shù)據(jù)就丟失了寄疏,相反若配置了副本集是牢,則同樣的數(shù)據(jù)會保存在副本服務(wù)器中(副本節(jié)點),一般副本集包含了一個主節(jié)點與多個副本節(jié)點陕截,必要時還會配置arbiter(仲裁節(jié)點)作為節(jié)點掛掉時投票用驳棱。

  • arbiter(仲裁節(jié)點):仲裁服務(wù)器本身不包含數(shù)據(jù),僅能在主節(jié)點故障時农曲,檢測所有副本服務(wù)器并選舉出新的主節(jié)點社搅,其實現(xiàn)方式是通過主節(jié)點、副本節(jié)點乳规、仲裁服務(wù)器之間的心跳(Heart beat)實現(xiàn)形葬。

2、MongoDB應(yīng)用場景

  • 網(wǎng)站數(shù)據(jù):適合實時的插入暮的,更新與查詢笙以,并具備網(wǎng)站實時數(shù)據(jù)存儲所需的復(fù)制及高度伸縮性。

  • 緩存:由于性能很高冻辩,也適合作為信息基礎(chǔ)設(shè)施的緩存層猖腕。在系統(tǒng)重啟之后,搭建的持久化緩存可以避免下層的數(shù)據(jù)源過載恨闪。

  • 大尺寸倘感、低價值的數(shù)據(jù):使用傳統(tǒng)的關(guān)系數(shù)據(jù)庫存儲一些數(shù)據(jù)時可能會比較貴,在此之前咙咽,很多程序員往往會選擇傳統(tǒng)的文件進行存儲老玛。

  • 高伸縮性的場景:非常適合由數(shù)十或者數(shù)百臺服務(wù)器組成的數(shù)據(jù)庫。

  • 用于對象及JSON數(shù)據(jù)的存儲:MongoDB的BSON數(shù)據(jù)格式非常適合文檔格式化的存儲及查詢钧敞。

3蜡豹、選用MongoDB的緣由

選用MongoDB的數(shù)據(jù)是以BSON的數(shù)據(jù)格式,高度伸縮方便擴展犁享,并且數(shù)據(jù)水平擴展非常簡單余素,支持海量數(shù)據(jù)存儲,性能強悍炊昆。

二桨吊、集群的監(jiān)測

1、監(jiān)測數(shù)據(jù)庫存儲統(tǒng)計信息

docker中進入mongos或shard實例凤巨,執(zhí)行以下命令:

docker exec -it mongos bash;
mongo --port 20001;
use admin;
db.auth("root","XXX");

說明:通過此命令视乐,可以查詢集群的成員的集合數(shù)量、索引數(shù)量等相關(guān)數(shù)據(jù)敢茁。13個Mongodb GUI可視化管理工具佑淀,總有一款適合你

db.stats();

2、查看數(shù)據(jù)庫的統(tǒng)計信息

說明:通過此命令彰檬,可以查看操作數(shù)量伸刃、內(nèi)存使用狀況谎砾、網(wǎng)絡(luò)io等

db.runCommand( { serverStatus: 1 } );

3捧颅、檢查復(fù)制集成員狀態(tài)景图,需要賬號密碼有clusterAdmin角色權(quán)限

rs.status();

三碉哑、基本的運維操作

1挚币、設(shè)置和查看慢查詢

# 設(shè)置慢查詢
db.setProfilingLevel(1,200);
# 查看慢查詢級別
db.getProfilingLevel();
# 查詢慢查詢?nèi)罩荆嗣钍轻槍τ谀骋粠爝M行設(shè)置
db.system.profile.find({ ns : 'dbName.collectionName'}).limit(10).sort( { ts : -1 } ).pretty();
# 設(shè)置慢查詢抽樣比例
db.setProfilingLevel(1, { sampleRate: 0.42 })
rs0:PRIMARY> db.setProfilingLevel(1, { sampleRate: 0.42 })
{
        "was" : 1,
        "slowms" : 200,
        "sampleRate" : 1,
        "ok" : 1,
        "operationTime" : Timestamp(1536309735, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1536309735, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}
rs0:PRIMARY>
rs0:PRIMARY> db.getProfilingStatus()
{
        "was" : 1,
        "slowms" : 200,
        "sampleRate" : 0.42,
        "operationTime" : Timestamp(1536309775, 1),
        "$clusterTime" : {
                "clusterTime" : Timestamp(1536309775, 1),
                "signature" : {
                        "hash" : BinData(0,"AAAAAAAAAAAAAAAAAAAAAAAAAAA="),
                        "keyId" : NumberLong(0)
                }
        }
}
 
 
注釋:
By default, sampleRate is set to 1.0, meaning all slow operations are profiled.
When sampleRate is set between 0 and 1, databases with profiling level 1 will
only profile a randomly sampled percentage of slow operations according to sampleRate.
 
sampleRate 參數(shù)的默認值是1.0扣典,即收集所有慢查詢妆毕,此參數(shù)的范圍值是0到1.當(dāng)級別設(shè)置為1會按照sampleRate 設(shè)置的值隨機抽取百分比的慢操作。
除了在運行時設(shè)置慢查詢外贮尖,還可以在命令模式下啟動時設(shè)置:
mongod --profile 1 --slowms 15 --slowOpSampleRate 0.5
也可以將參數(shù)放置到配置文件:
profile=1
slowms=200
slowOpSampleRate=0.5
 
profiler的數(shù)據(jù)存儲在MongoDB中的system.profile collection笛粘。
在主庫修改system.profile的集合的大小:
步驟如下:
Disable profiling.
Drop the system.profile collection.
Create a new system.profile collection.
Re-enable profiling.
 
--修改為4M:
db.setProfilingLevel(0)
db.system.profile.drop()
db.createCollection( "system.profile", { capped: true, size:4000000 } )
db.setProfilingLevel(1)
 
--慢查詢操作的查詢:
 
--慢查詢操作的可視化:
mongoDB的慢查詢操作可以結(jié)合PMM的監(jiān)控PMM-QAN远舅,但是支持MongoDB 3.2及以上版本闰蛔。
需要2個步驟:
1.設(shè)置必需的賬號
2.開啟profiler。
在MongoDB設(shè)置賬號和權(quán)限:
db.getSiblingDB("admin").createUser({
    user: "mongodb_exporter",
    pwd: "mongodb_exporter",
    roles: [
        { role: "clusterMonitor", db: "admin" },
        { role: "read", db: "local" }
    ]
})
 
開啟profiler:
$ mongod --dbpath=DATABASEDIR --profile 2 --slowms 200 --rateLimit 100
或者寫入配置文件:
operationProfiling:
   slowOpThresholdMs: 200
   mode: slowOp
   rateLimit: 100
 
https://docs.mongodb.com/manual/tutorial/manage-the-database-profiler/
https://www.percona.com/doc/percona-monitoring-and-management/conf-mongodb.html

2图柏、查看執(zhí)行操作時間較長的動作

db.currentOp({"active" : true,"secs_running" : { "$gt" : 2000 }});

3序六、動態(tài)調(diào)整日志級別和設(shè)置緩存大小

# 設(shè)置日志級別參數(shù)
db.adminCommand( { "getParameter": 1, "logLevel":1});
# 設(shè)置cache大小參數(shù)
db.adminCommand( { "setParameter": 1, "wiredTigerEngineRuntimeConfig": "cache_size=4G"});

4、添加和移除復(fù)制集成員

# 查看復(fù)制集成員
rs.status().members;
# 添加成員
rs.add('127.0.0.1:20001')蚤吹;
# 移除成員
rs.remove('127.0.0.1:20001')例诀;

刪除實錄:

myReplSet:PRIMARY> rs.remove('172.26.189.240:27017');
{
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1646553977, 2),
        "signature" : {
            "hash" : BinData(0,"WEtmdj3tqkwWt3zAsrr3/yWrQnw="),
            "keyId" : NumberLong("7070066341641715716")
        }
    },
    "operationTime" : Timestamp(1646553977, 2)
}
myReplSet:PRIMARY> rs.status().members;
[
    {
        "_id" : 0,
        "name" : "172.26.189.239:27017",
        "health" : 1,
        "state" : 1,
        "stateStr" : "PRIMARY",
        "uptime" : 78972,
        "optime" : {
            "ts" : Timestamp(1646553977, 2),
            "t" : NumberLong(6)
        },
        "optimeDate" : ISODate("2022-03-06T08:06:17Z"),
        "lastAppliedWallTime" : ISODate("2022-03-06T08:06:17.544Z"),
        "lastDurableWallTime" : ISODate("2022-03-06T08:06:17.544Z"),
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "infoMessage" : "",
        "electionTime" : Timestamp(1646475275, 1),
        "electionDate" : ISODate("2022-03-05T10:14:35Z"),
        "configVersion" : 2,
        "configTerm" : 6,
        "self" : true,
        "lastHeartbeatMessage" : ""
    },
    {
        "_id" : 2,
        "name" : "172.26.189.241:27017",
        "health" : 1,
        "state" : 7,
        "stateStr" : "ARBITER",
        "uptime" : 78970,
        "lastHeartbeat" : ISODate("2022-03-06T08:06:25.545Z"),
        "lastHeartbeatRecv" : ISODate("2022-03-06T08:06:25.549Z"),
        "pingMs" : NumberLong(0),
        "lastHeartbeatMessage" : "",
        "syncSourceHost" : "",
        "syncSourceId" : -1,
        "infoMessage" : "",
        "configVersion" : 2,
        "configTerm" : 6
    }
]
myReplSet:PRIMARY>
#把它重新加回來
myReplSet:PRIMARY> rs.add('172.26.189.240:27017');
{
    "operationTime" : Timestamp(1646554027, 1),
    "ok" : 0,
    "errmsg" : "Rejecting reconfig where the new config has a PSA topology and the secondary is electable, but the old config contains only one writable node. Refer to https://docs.mongodb.com/manual/reference/method/rs.reconfigForPSASet/ for next steps on reconfiguring a PSA set.",
    "code" : 103,
    "codeName" : "NewReplicaSetConfigurationIncompatible",
    "$clusterTime" : {
        "clusterTime" : Timestamp(1646554027, 1),
        "signature" : {
            "hash" : BinData(0,"ULbeSKepCJCF9oIsjiks2coqWsI="),
            "keyId" : NumberLong("7070066341641715716")
        }
    }
}
myReplSet:PRIMARY>
#根據(jù)https://docs.mongodb.com/manual/reference/method/rs.reconfigForPSASet/ 提示
#第一步,使用rs.conf()方法檢索包含副本集當(dāng)前配置的文檔裁着,并將該文檔存儲在局部變量cfg中繁涂。
myReplSet:PRIMARY> cfg = rs.conf();
{
    "_id" : "myReplSet",
    "version" : 2,
    "term" : 6,
    "protocolVersion" : NumberLong(1),
    "writeConcernMajorityJournalDefault" : true,
    "members" : [
        {
            "_id" : 0,
            "host" : "172.26.189.239:27017",
            "arbiterOnly" : false,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 1,
            "tags" : {
                
            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        },
        {
            "_id" : 2,
            "host" : "172.26.189.241:27017",
            "arbiterOnly" : true,
            "buildIndexes" : true,
            "hidden" : false,
            "priority" : 0,
            "tags" : {
                
            },
            "slaveDelay" : NumberLong(0),
            "votes" : 1
        }
    ],
    "settings" : {
        "chainingAllowed" : true,
        "heartbeatIntervalMillis" : 2000,
        "heartbeatTimeoutSecs" : 10,
        "electionTimeoutMillis" : 10000,
        "catchUpTimeoutMillis" : -1,
        "catchUpTakeoverDelayMillis" : 30000,
        "getLastErrorModes" : {
            
        },
        "getLastErrorDefaults" : {
            "w" : 1,
            "wtimeout" : 0
        },
        "replicaSetId" : ObjectId("621debd68919ec9f88b0feba")
    }
}
第二步,將新的次要成員添加到members數(shù)組中二驰。在這種配置中扔罪,新的輔助節(jié)點被添加到memberIndex 1。memberIndex與數(shù)組索引相同桶雀。有關(guān)其他設(shè)置矿酵,請參閱副本集配置設(shè)置。
例子:
cfg["members"] = [
  {
     "_id" : 0,
     "host" : "mongodb0.example.net:27017",
     "arbiterOnly" : false,
     "buildIndexes" : true,
     "hidden" : false,
     "priority" : 1,
     "tags" : {},
     "secondaryDelaySecs" : Long("0"),
     "votes" : 1
  },
  {
     "_id" : 1,
     "host" : "mongodb2.example.net:27017",
     "arbiterOnly" : false,
     "buildIndexes" : true,
     "hidden" : false,
     "priority" : 2,
     "tags" : {},
     "secondaryDelaySecs" : Long("0"),
     "votes" : 1
  },
  {
     "_id" : 2,
     "host" : "mongodb2.example.net:27017",
     "arbiterOnly" : true,
     "buildIndexes" : true,
     "hidden" : false,
     "priority" : 0,
     "tags" : {},
     "secondaryDelaySecs" : Long("0"),
     "votes" : 1
  }
]

將自己的對應(yīng)數(shù)據(jù)替換進去矗积,注意 "secondaryDelaySecs" : Long("0"), 和你的配置"slaveDelay" : NumberLong(0),進行對比要和自己的配置一致全肮,才不會報錯
uncaught exception: ReferenceError: Long is not defined :
@(shell):10:6

自己修改后的語句

cfg["members"] = [
  {
     "_id" : 0,
     "host" : "172.26.189.239:27017",
     "arbiterOnly" : false,
     "buildIndexes" : true,
     "hidden" : false,
     "priority" : 1,
     "tags" : {},
     "slaveDelay" : NumberLong(0),
     "votes" : 1
  },
  {
     "_id" : 1,
     "host" : "172.26.189.240:27017",
     "arbiterOnly" : false,
     "buildIndexes" : true,
     "hidden" : false,
     "priority" : 2,
     "tags" : {},
     "slaveDelay" : NumberLong(0),
     "votes" : 1
  },
  {
     "_id" : 2,
     "host" : "172.26.189.241:27017",
     "arbiterOnly" : true,
     "buildIndexes" : true,
     "hidden" : false,
     "priority" : 0,
     "tags" : {},
     "slaveDelay" : NumberLong(0),
     "votes" : 1
  }
]

第三步,使用memberIndex 1和修改后的cfg調(diào)用rs.reconfigorpsaset()方法棘捣。memberIndex是新成員在members數(shù)組中的數(shù)組位置辜腺。
rs.reconfigForPSASet(1, cfg);

myReplSet:PRIMARY> rs.reconfigForPSASet(1, cfg);
Running first reconfig to give member at index 1 { votes: 1, priority: 0 }
Running second reconfig to give member at index 1 { priority: 2 }
{
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1646557821, 1),
        "signature" : {
            "hash" : BinData(0,"MliKIFIdMKJz/dJxWsGby0blJRg="),
            "keyId" : NumberLong("7070066341641715716")
        }
    },
    "operationTime" : Timestamp(1646557821, 1)
}

成功重新配置后,副本集配置如下所示:
myReplSet:PRIMARY> rs.status()
{
    "set" : "myReplSet",
    "date" : ISODate("2022-03-06T09:10:39.312Z"),
    "myState" : 2,
    "term" : NumberLong(7),
    "syncSourceHost" : "172.26.189.240:27017",
    "syncSourceId" : 1,
    "heartbeatIntervalMillis" : NumberLong(2000),
    "majorityVoteCount" : 2,
    "writeMajorityCount" : 2,
    "votingMembersCount" : 3,
    "writableVotingMembersCount" : 2,
    "optimes" : {
        "lastCommittedOpTime" : {
            "ts" : Timestamp(1646557831, 2),
            "t" : NumberLong(7)
        },
        "lastCommittedWallTime" : ISODate("2022-03-06T09:10:31.645Z"),
        "readConcernMajorityOpTime" : {
            "ts" : Timestamp(1646557831, 2),
            "t" : NumberLong(7)
        },
        "readConcernMajorityWallTime" : ISODate("2022-03-06T09:10:31.645Z"),
        "appliedOpTime" : {
            "ts" : Timestamp(1646557831, 2),
            "t" : NumberLong(7)
        },
        "durableOpTime" : {
            "ts" : Timestamp(1646557831, 2),
            "t" : NumberLong(7)
        },
        "lastAppliedWallTime" : ISODate("2022-03-06T09:10:31.645Z"),
        "lastDurableWallTime" : ISODate("2022-03-06T09:10:31.645Z")
    },
    "lastStableRecoveryTimestamp" : Timestamp(1646557831, 2),
    "electionParticipantMetrics" : {
        "votedForCandidate" : true,
        "electionTerm" : NumberLong(7),
        "lastVoteDate" : ISODate("2022-03-06T09:10:31.640Z"),
        "electionCandidateMemberId" : 1,
        "voteReason" : "",
        "lastAppliedOpTimeAtElection" : {
            "ts" : Timestamp(1646557821, 1),
            "t" : NumberLong(6)
        },
        "maxAppliedOpTimeInSet" : {
            "ts" : Timestamp(1646557821, 1),
            "t" : NumberLong(6)
        },
        "priorityAtElection" : 1,
        "newTermStartDate" : ISODate("2022-03-06T09:10:31.645Z"),
        "newTermAppliedDate" : ISODate("2022-03-06T09:10:32.645Z")
    },
    "members" : [
        {
            "_id" : 0,
            "name" : "172.26.189.239:27017",
            "health" : 1,
            "state" : 2,
            "stateStr" : "SECONDARY",
            "uptime" : 82824,
            "optime" : {
                "ts" : Timestamp(1646557831, 2),
                "t" : NumberLong(7)
            },
            "optimeDate" : ISODate("2022-03-06T09:10:31Z"),
            "lastAppliedWallTime" : ISODate("2022-03-06T09:10:31.645Z"),
            "lastDurableWallTime" : ISODate("2022-03-06T09:10:31.645Z"),
            "syncSourceHost" : "172.26.189.240:27017",
            "syncSourceId" : 1,
            "infoMessage" : "",
            "configVersion" : 4,
            "configTerm" : 7,
            "self" : true,
            "lastHeartbeatMessage" : ""
        },
        {
            "_id" : 1,
            "name" : "172.26.189.240:27017",
            "health" : 1,
            "state" : 1,
            "stateStr" : "PRIMARY",
            "uptime" : 17,
            "optime" : {
                "ts" : Timestamp(1646557831, 2),
                "t" : NumberLong(7)
            },
            "optimeDurable" : {
                "ts" : Timestamp(1646557831, 2),
                "t" : NumberLong(7)
            },
            "optimeDate" : ISODate("2022-03-06T09:10:31Z"),
            "optimeDurableDate" : ISODate("2022-03-06T09:10:31Z"),
            "lastAppliedWallTime" : ISODate("2022-03-06T09:10:31.645Z"),
            "lastDurableWallTime" : ISODate("2022-03-06T09:10:31.645Z"),
            "lastHeartbeat" : ISODate("2022-03-06T09:10:39.020Z"),
            "lastHeartbeatRecv" : ISODate("2022-03-06T09:10:37.644Z"),
            "pingMs" : NumberLong(37),
            "lastHeartbeatMessage" : "",
            "syncSourceHost" : "",
            "syncSourceId" : -1,
            "infoMessage" : "",
            "electionTime" : Timestamp(1646557831, 1),
            "electionDate" : ISODate("2022-03-06T09:10:31Z"),
            "configVersion" : 4,
            "configTerm" : 7
        },
        {
            "_id" : 2,
            "name" : "172.26.189.241:27017",
            "health" : 1,
            "state" : 7,
            "stateStr" : "ARBITER",
            "uptime" : 82822,
            "lastHeartbeat" : ISODate("2022-03-06T09:10:38.648Z"),
            "lastHeartbeatRecv" : ISODate("2022-03-06T09:10:37.647Z"),
            "pingMs" : NumberLong(0),
            "lastHeartbeatMessage" : "",
            "syncSourceHost" : "",
            "syncSourceId" : -1,
            "infoMessage" : "",
            "configVersion" : 4,
            "configTerm" : 7
        }
    ],
    "ok" : 1,
    "$clusterTime" : {
        "clusterTime" : Timestamp(1646557831, 2),
        "signature" : {
            "hash" : BinData(0,"JGx6vyRqndKwYUrHNvBNz2z/9wQ="),
            "keyId" : NumberLong("7070066341641715716")
        }
    },
    "operationTime" : Timestamp(1646557831, 2)
}
myReplSet:SECONDARY>

5、設(shè)置數(shù)據(jù)庫和集合分片

# 在mongos admin庫設(shè)置庫允許分片
sh.enableSharding("dbName");
# 在mongos 的admin庫設(shè)置集合分片片鍵
sh.shardCollection("dbName.collectionName", { filedName: 1} );

6评疗、添加和移除分片

# 查看分片狀態(tài)
sh.status()测砂;
# 在mongos執(zhí)行添加分片(可以為單個實例或復(fù)制集)
db.runCommand( { removeShard: "shardName" } );
db.runCommand({addshard:"rs1/ip-1:20001,ip-2:20001,ip-3:20001"});
# 在mongos執(zhí)行移除分片
db.runCommand( { removeShard: "shard3" } )壤巷;
# 在mongos執(zhí)行刷新mongos配置信息
db.runCommand("flushRouterConfig"))邑彪;
  • 說明:移除分片命令至少執(zhí)行兩次才能成功刪除瞧毙,執(zhí)行到state為completed才真正刪除胧华,否則就是沒用刪除成功,該分片處于{"draining" : true}狀態(tài)宙彪,該狀態(tài)下不但該分片沒用刪除成功矩动,而且還影響接下來刪除其他分片操作,遇到該狀態(tài)再執(zhí)行一次removeshard即可释漆,最好就是刪除分片時一直重復(fù)執(zhí)行刪除命令悲没,直到state為completed; 還有一個需要注意的地方就是:被成功刪除的分片如果想要再加入集群時男图,必須將data數(shù)據(jù)目錄清理干凈才可以再加入集群示姿,否則即使能加入成功也不會存儲數(shù)據(jù),集合都不會被創(chuàng)建逊笆。

另外:在刪除分片的時有可能整個過程出現(xiàn)無限{"draining" : true}狀態(tài)栈戳,等多久還是這樣,而且分片上面的塊一個都沒有移動到別的分片难裆,解決辦法是:在config的config數(shù)據(jù)庫的shard集合中找到該分片的信息子檀,并將draining字段由True改為False,再繼續(xù)試著刪除操作” 上面這句會立即返回,實際在后臺執(zhí)行乃戈。在數(shù)據(jù)移除的過程當(dāng)中褂痰,一定要注意實例的日志信息,可能出現(xiàn)數(shù)據(jù)塊在遷移的過程中症虑,始終找不到邊界條件缩歪,導(dǎo)致一直數(shù)據(jù)遷移不成功,一直重試谍憔,解決方案是刪除邊界數(shù)據(jù)匪蝙,重啟實例。

如果此分片為主分片韵卤,需要先遷移主分片骗污。db.runCommand( { movePrimary: "XXX", to: "other" });在完成刪除后沈条,所有mongos上運行下面命令需忿,再對外提供服務(wù),當(dāng)然也可以重新啟動所有mongos實例 。

7屋厘、數(shù)據(jù)的導(dǎo)入導(dǎo)出

# 導(dǎo)出允許指定導(dǎo)出條件和字段
mongoexport -h 127.0.0.1 --port 20001 -uxxx -pxxx -d xxx -c mobileIndex -o XXX.txt 
mongoimport -h 127.0.0.1 --port 20001 -uxxx -pxxx -d xxx -c mobileIndex --file XXX.txt

四涕烧、MongoDB數(shù)據(jù)遷移

1、遷移復(fù)制集當(dāng)中的成員

  • 關(guān)閉 mongod 實例,為了確保安全關(guān)閉,使用 shutdown 命令汗洒;
  • 將數(shù)據(jù)目錄(即 dbPath )轉(zhuǎn)移到新機器上议纯;
  • 在新機器上啟動 mongod,其中節(jié)點的數(shù)據(jù)目錄為copy的文件目錄 溢谤;
  • 連接到復(fù)制集當(dāng)前的主節(jié)點上瞻凤;
    如果新節(jié)點的地址發(fā)生變化,使用 rs.reconfig() 更新 復(fù)制集配置文檔 ;舉例,下面的命令過程將成員中位于第 2 位的地址進行更新:
cfg = rs.conf()
cfg.members[2].host = "127.0.0.1:27017"
rs.reconfig(cfg)

使用 rs.conf() 確認使用了新的配置. 等待所有成員恢復(fù)正常,使用 rs.status() 檢測成員狀態(tài)世杀。

2阀参、遷移復(fù)制集主節(jié)點

在遷移主節(jié)點的時候,需要復(fù)制集選舉出一個新的主節(jié)點,在進行選舉的時候,復(fù)制集將讀寫,通常,這只會持續(xù)很短的時間,不過,應(yīng)該盡可能在影響較小的時間段內(nèi)遷移主節(jié)點.

主節(jié)點降級,以使得正常的 failover開始.要將主節(jié)點降級,連接到一個主節(jié)點,使用 replSetStepDown方法或者使用rs.stepDown()方法,下面的例子使用了 rs.stepDown()方法進行降級:

rs.stepDown()

等主節(jié)點降級為從節(jié)點,另一個成員成為 PRIMARY 之后,可以按照 “遷移復(fù)制集的一個成員”遷移這個降級了的節(jié)點.可以使用 rs.status()來確認狀態(tài)的改變。

3瞻坝、從復(fù)制集其他節(jié)點恢復(fù)數(shù)據(jù)

MongoDB 通過復(fù)制集能保證高可靠的數(shù)據(jù)存儲蛛壳,通常生產(chǎn)環(huán)境建議使用「3節(jié)點復(fù)制集」,這樣即使其中一個節(jié)點崩潰了無法啟動所刀,我們可以直接將其數(shù)據(jù)清掉衙荐,重新啟動后,以全新的 Secondary 節(jié)點加入復(fù)制集浮创,或者是將其他節(jié)點的數(shù)據(jù)復(fù)制過來忧吟,重新啟動節(jié)點,它會自動的同步數(shù)據(jù)蒸矛,這樣也就達到了恢復(fù)數(shù)據(jù)的目的瀑罗。

關(guān)閉需要數(shù)據(jù)同步的節(jié)點

docker stop node;  # docker環(huán)境中
db.shutdownServer({timeoutSecs: 60}); # 非docker環(huán)境

拷貝目標節(jié)點機器的數(shù)據(jù)存儲目錄(/dbPath)到當(dāng)前機器的指定目錄。

scp 目標節(jié)點 shard/data -> 當(dāng)前節(jié)點 shard/data

當(dāng)前節(jié)點以復(fù)制過來的數(shù)據(jù)文件啟動節(jié)點

將新的節(jié)點添加到復(fù)制集

# 進入復(fù)制集的主節(jié)點雏掠,執(zhí)行添加新的節(jié)點命令
rs.add("hostNameNew:portNew"); 
# 等待所有成員恢復(fù)正常,檢測成員狀態(tài)
rs.status();
# 移除原來的節(jié)點
rs.remove("hostNameOld>:portOld"); 

五斩祭、MongoDB線上問題場景解決

1、MongoDB 新建索引導(dǎo)致庫被鎖

  • 問題說明:某線上千萬級別集合乡话,為優(yōu)化業(yè)務(wù)摧玫,直接執(zhí)行新建索引命令,導(dǎo)致整個庫被鎖绑青,應(yīng)用服務(wù)出現(xiàn)不可用诬像。

  • 解決方案:找出此操作進程,并且殺死闸婴。改為后臺新建索引坏挠,速度會很慢,但是不會影響業(yè)務(wù)邪乍,該索引只會在新建完成之后降狠,才會生效对竣;

# 查詢運行時間超過200ms操作     
db.currentOp({"active" : true,"secs_running" : { "$gt" : 2000 }}) ;
# 殺死執(zhí)行時間過長操作操作
db.killOp(opid)
# 后臺新建索引
db.collectionNmae.ensureIndex({filedName:1}, {background:true});

2榜配、MongoDB沒有限制內(nèi)存否纬,導(dǎo)致實例退出

  • 問題說明:生產(chǎn)環(huán)境某臺機器啟動多個mongod實例,運行一段時間過后蛋褥,進程莫名被殺死临燃;

  • 解決方案:現(xiàn)在MongoDB使用WiredTiger作為默認存儲引擎,MongoDB同時使用WiredTiger內(nèi)部緩存和文件系統(tǒng)緩存烙心。從3.4開始膜廊,WiredTiger內(nèi)部緩存默認使用較大的一個:50%(RAM - 1 GB),或256 MB弃理。例如溃论,在總共4GB RAM的系統(tǒng)上,WiredTiger緩存將使用1.5GB的RAM()痘昌。相反,具有總共1.25 GB RAM的系統(tǒng)將為WiredTiger緩存分配256 MB炬转,因為這超過總RAM的一半減去1千兆字節(jié)()辆苔。

#如果一臺機器存在多個實例,在內(nèi)存不足的情景在扼劈,操作系統(tǒng)會殺死部分進程驻啤;
0.5 * (4 GB - 1GB) = 1.5 GB``0.5 * (1.25 GB - 1 GB) = 128 MB < 256 MB。

# 要調(diào)整WiredTiger內(nèi)部緩存的大小荐吵,調(diào)節(jié)cache規(guī)模不需要重啟服務(wù)骑冗,我們可以動態(tài)調(diào)整:
db.adminCommand( { "setParameter": 1, "wiredTigerEngineRuntimeConfig": "cache_size=xxG"})

3、MongoDB刪除數(shù)據(jù)先煎,不釋放磁盤空間

  • 問題說明:在刪除大量數(shù)據(jù)(本人操作的數(shù)據(jù)量在2000萬+)的情景下,并且在生產(chǎn)環(huán)境中請求量較大,此時機器的cpu負載會顯得很高嚼蚀,甚至機器卡頓無法操作献雅,這樣的操作應(yīng)該謹慎分批量操作;在刪除命令執(zhí)行結(jié)束之后占锯,發(fā)現(xiàn)磁盤的數(shù)據(jù)量大小并沒有改變袒哥。

  • 解決方案:

    • 方案一:我們可以使用MongoDB提供的在線數(shù)據(jù)收縮的功能,通過Compact命令db.collectionName.runCommand("compact")進行Collection級別的數(shù)據(jù)收縮消略,去除集合所在文件碎片堡称。此命令是以O(shè)nline的方式提供收縮,收縮的同時會影響到線上的服務(wù)艺演。為了解決這個問題却紧,可以先在從節(jié)點執(zhí)行磁盤整理命令婿失,操作結(jié)束后,再切換主節(jié)點啄寡,將原來的主節(jié)點變?yōu)閺墓?jié)點豪硅,重新執(zhí)行Compact命令即可。

    • 方案二:使用從節(jié)點重新同步挺物,secondary節(jié)點重同步懒浮,刪除secondary節(jié)點中指定數(shù)據(jù),使之與primary重新開始數(shù)據(jù)同步识藤。當(dāng)副本集成員數(shù)據(jù)太過陳舊砚著,也可以使用重新同步。數(shù)據(jù)的重新同步與直接復(fù)制數(shù)據(jù)文件不同痴昧,MongoDB會只同步數(shù)據(jù)稽穆,因此重同步完成后的數(shù)據(jù)文件是沒有空集合的,以此實現(xiàn)了磁盤空間的回收赶撰。

  • 針對一些特殊情況舌镶,不能下線secondary節(jié)點的,可以新增一個節(jié)點到副本集中豪娜,然后secondary就自動開始數(shù)據(jù)的同步了餐胀。總的來說瘤载,重同步的方法是比較好的否灾,第一基本不會阻塞副本集的讀寫,第二消耗的時間相對前兩種比較短鸣奔。

    • 若是primary節(jié)點墨技,先強制將之變?yōu)閟econdary節(jié)點,否則跳過此步驟:rs.stepdown(120)挎狸;
    • 然后在primary上刪除secondary節(jié)點:rs.remove("IP:port");
    • 刪除secondary節(jié)點dbpath下的所有文件
    • 將節(jié)點重新加入集群扣汪,然后使之自動進行數(shù)據(jù)的同步:rs.add("IP:port");
    • 等數(shù)據(jù)同步完成后,循環(huán)1-4的步驟可以將集群中所有節(jié)點的磁盤空間釋放

4伟叛、MongoDB機器負載極高

  • 問題說明:此情景是在客戶請求較大的情景性私痹,由于部署MongoDB的機器包含一主一從,MongoDB使得IO100%统刮,數(shù)據(jù)庫阻塞紊遵,出現(xiàn)大量慢查詢,進而導(dǎo)致機器負載極高侥蒙,應(yīng)用服務(wù)完全不可用暗膜。

  • 解決方案:在沒有機器及時擴容的狀況下,首要任務(wù)便是減小機器的IO鞭衩,在一臺機器出現(xiàn)一主一從学搜,在大量數(shù)據(jù)寫入的情況下娃善,會互相搶占IO資源。于是此時摒棄了MongoDB高可用的特點瑞佩,摘掉了復(fù)制集當(dāng)中的從節(jié)點聚磺,保證每臺機器只有一個節(jié)點可以占用磁盤資源。之后炬丸,機器負載立馬下來瘫寝,服務(wù)變?yōu)檎?捎脿顟B(tài)稠炬,但是此時MongoDB無法保證數(shù)據(jù)的完整性焕阿,一旦有主節(jié)點掛掉便會丟失數(shù)據(jù)。

此方案只是臨時方法首启,根本解決是可以增加機器的內(nèi)存暮屡、使用固態(tài)硬盤,或者采用增加分片集來減少單個機器的讀寫壓力毅桃。

# 進入主節(jié)點褒纲,執(zhí)行移除成員的命令
rs.remove("127.0.0.1:20001");
# 注意:切勿直接關(guān)停實例

5、MongoDB分片鍵選擇不當(dāng)導(dǎo)致熱讀熱寫

  • 問題說明:生產(chǎn)環(huán)境中疾嗅,某一集合的片鍵使用了與_id生成方式相似外厂,含有時間序列的字段作為升序片鍵,導(dǎo)致數(shù)據(jù)寫入時都在一個數(shù)據(jù)塊代承,隨著數(shù)據(jù)量增大,會造成數(shù)據(jù)遷移到前面的分區(qū)渐扮,造成系統(tǒng)資源的占用论悴,偶爾出現(xiàn)慢查詢。

  • 解決方案:臨時方案設(shè)置數(shù)據(jù)遷移的窗口墓律,放在在正常的時間區(qū)段膀估,對業(yè)務(wù)造成影響。根本解決是更換片鍵耻讽。

# 連接mongos實例察纯,執(zhí)行以下命令
db.settings.update({ _id : "balancer" }, { $set : { activeWindow : { start : "23:00", stop : "4:00" } } }, true );
# 查看均衡窗口
sh.getBalancerWindow()针肥;

六饼记、MongoDB優(yōu)化建議

1、應(yīng)用層面優(yōu)化

查詢優(yōu)化:確認你的查詢是否充分利用到了索引慰枕,用explain命令查看一下查詢執(zhí)行的情況具则,添加必要的索引,避免掃表操作具帮。

  • 合理設(shè)計分片鍵:

    • 增量sharding-key:適合于可劃分范圍的字段博肋,比如integer低斋、float、date類型的匪凡,查詢時比較快膊畴。
    • 隨機sharding-key: 適用于寫操作頻繁的場景,而這種情況下如果在一個shard上進行會使得這個shard負載比其他高,不夠均衡病游,故而希望能hash查詢key唇跨,將寫分布在多個shard上進行,考慮復(fù)合key作為sharding key,總的原則是查詢快礁遵,盡量減少跨shard查詢轻绞,balance均衡次數(shù)少;
    • 單一遞增的sharding key佣耐,可能會造成寫數(shù)據(jù)全部在最后一片上政勃,最后一片的寫壓力增大,數(shù)據(jù)量增大兼砖,會造成數(shù)據(jù)遷移到前面的分區(qū)奸远。MongoDB默認是單條記錄16M,尤其在使用GFS的時候讽挟,一定要注意shrading-key的設(shè)計懒叛。不合理的sharding-key會出現(xiàn),多個文檔耽梅,在一個chunks上薛窥,同時,因為GFS中存貯的往往是大文件眼姐,導(dǎo)致MongoDB在做balance的時候無法通過sharding-key來把這多個文檔分開到不同的shard上诅迷, 這時候MongoDB會不斷報錯最后導(dǎo)致MongoDB倒掉。解決辦法:加大chunks大兄谄臁(治標)罢杉,設(shè)計合理的sharding-key(治本)。

通過profile來監(jiān)控數(shù)據(jù):進行優(yōu)化查看當(dāng)前是否開啟profile功能,用命令db.getProfilingLevel() 返回level等級贡歧,值為0|1|2滩租,分別代表意思:0代表關(guān)閉,1代表記錄慢命令利朵,2代表全部律想。開啟profile功能命令為 db.setProfilingLevel(level); #level等級,值level為1的時候哗咆,慢命令默認值為100ms蜘欲,更改為db.setProfilingLevel(level,slowms)如db.setProfilingLevel(1,50)這樣就更改為50毫秒通過db.system.profile.find() 查看當(dāng)前的監(jiān)控日志。

2晌柬、硬件層面優(yōu)化

  • 2.1 確定熱數(shù)據(jù)大小
    可能你的數(shù)據(jù)集非常大姥份,但是這并不那么重要郭脂,重要的是你的熱數(shù)據(jù)集有多大,你經(jīng)常訪問的數(shù)據(jù)有多大(包括經(jīng)常訪問的數(shù)據(jù)和所有索引數(shù)據(jù))澈歉。使用MongoDB展鸡,你最好保證你的熱數(shù)據(jù)在你機器的內(nèi)存大小之下,保證內(nèi)存能容納所有熱數(shù)據(jù)埃难;
  • 2.2 選擇正確的文件系統(tǒng)
    MongoDB的數(shù)據(jù)文件是采用的預(yù)分配模式莹弊,并且在Replication里面,Master和Replica Sets的非Arbiter節(jié)點都是會預(yù)先創(chuàng)建足夠的空文件用以存儲操作日志涡尘。這些文件分配操作在一些文件系統(tǒng)上可能會非常慢忍弛,導(dǎo)致進程被Block。所以我們應(yīng)該選擇那些空間分配快速的文件系統(tǒng)考抄。這里的結(jié)論是盡量不要用ext3细疚,用ext4或xfs;

3川梅、架構(gòu)上的優(yōu)化

盡可能讓主從節(jié)點分攤在不同的機器上疯兼,避免IO操作的與MongoDB在同一臺機器;

七贫途、總結(jié)

MongoDB具有高性能吧彪、易擴展、易上手等特點丢早,在正確使用的情況下姨裸,其本身性能還是非常強悍,在一些關(guān)鍵點如片鍵的選擇怨酝、內(nèi)存的大小和磁盤IO啦扬,往往是限制其性能的最大瓶頸。針對于片鍵凫碌,在業(yè)務(wù)系統(tǒng)初期,可以先不對集合進行數(shù)據(jù)分片胃榕,因為分片鍵一旦確定就無法修改盛险,后期可根據(jù)業(yè)務(wù)系統(tǒng)的情況,認真篩選字段勋又。

一般情況下苦掘,不建議使用升序片鍵(是一種隨著時間穩(wěn)定增長的字段,自增長的主鍵是升序鍵 ),因為這個會導(dǎo)致局部的熱讀熱寫楔壤,不能發(fā)揮分片集群的真正實力鹤啡。建議使用hash片鍵或者隨機分發(fā)的片鍵,這樣可以保證數(shù)據(jù)的均勻分發(fā)在分片節(jié)點蹲嚣;針對于內(nèi)存递瑰,建議內(nèi)存的大小能夠包含熱數(shù)據(jù)的大小加索引大小祟牲,保證內(nèi)存能容納所有熱數(shù)據(jù) 。

針對于磁盤資源抖部,MongoDB的高速讀寫是以磁盤的IO作為基礎(chǔ)说贝,為了保證其性能,建議將主從節(jié)點以及高IO的應(yīng)用分離慎颗,以保證IO資源盡可能不存在搶占乡恕。

轉(zhuǎn)載自:http://www.reibang.com/p/f05f65d3a1dc

以上內(nèi)容僅作為個人備忘。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末俯萎,一起剝皮案震驚了整個濱河市傲宜,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌夫啊,老刑警劉巖函卒,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異涮母,居然都是意外死亡谆趾,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進店門叛本,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沪蓬,“玉大人,你說我怎么就攤上這事来候□尾妫” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵营搅,是天一觀的道長云挟。 經(jīng)常有香客問我,道長转质,這世上最難降的妖魔是什么园欣? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任休蟹,我火速辦了婚禮赂弓,結(jié)果婚禮上盈魁,老公的妹妹穿的比我還像新娘。我一直安慰自己赤套,他們只是感情好于毙,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布脖旱。 她就那樣靜靜地躺著萌庆,像睡著了一般币旧。 火紅的嫁衣襯著肌膚如雪吹菱。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天占遥,我揣著相機與錄音输瓜,去河邊找鬼尤揣。 笑死,一個胖子當(dāng)著我的面吹牛负芋,可吹牛的內(nèi)容都是我干的示罗。 我是一名探鬼主播,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼迟赃!你這毒婦竟也來了纤壁?” 一聲冷哼從身側(cè)響起酌媒,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤秒咨,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后雨席,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體菩咨,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年抽米,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片云茸。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖罢低,靈堂內(nèi)的尸體忽然破棺而出查辩,到底是詐尸還是另有隱情网持,我是刑警寧澤宜岛,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布功舀,位于F島的核電站萍倡,受9級特大地震影響辟汰,放射性物質(zhì)發(fā)生泄漏帖汞。R本人自食惡果不足惜所意,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一扶踊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧秧耗,春花似錦备籽、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至杂抽,卻和暖如春诈唬,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背缩麸。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工铸磅, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人杭朱。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓阅仔,卻偏偏與公主長得像,于是被迫代替她去往敵國和親弧械。 傳聞我的和親對象是個殘疾皇子八酒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355

推薦閱讀更多精彩內(nèi)容