91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

MongoDB一次節點宕機引發的思考是怎樣的

發布時間:2021-09-29 11:22:55 來源:億速云 閱讀:159 作者:柒染 欄目:數據庫

本篇文章為大家展示了MongoDB一次節點宕機引發的思考是怎樣的,內容簡明扼要并且容易理解,絕對能使你眼前一亮,通過這篇文章的詳細介紹希望你能有所收獲。

簡介

最近一個 MongoDB 集群環境中的某節點異常下電了,導致業務出現了中斷,隨即又恢復了正常。

通過ELK 告警也監測到了業務報錯日志。

運維部對于節點下電的原因進行了排查,發現僅僅是資源分配上的一個失誤導致。 在解決了問題之后,大家也對這次中斷的也提出了一些問題:

"當前的 MongoDB集群 采用了分片副本集的架構,其中主節點發生故障會產生多大的影響?"

"MongoDB 副本集不是能自動倒換嗎,這個是不是秒級的?"

帶著這些問題,下面針對副本集的自動Failover機制做一些分析。

日志分析

首先可以確認的是,這次掉電的是一個副本集上的主節點,在掉電的時候,主備關系發生了切換。

從另外的兩個備節點找到了對應的日志:

備節點1的日志

2019-05-06T16:51:11.766+0800 I REPL [ReplicationExecutor] Starting an election, since we've seen no PRIMARY in the past 10000ms 2019-05-06T16:51:11.766+0800 I REPL [ReplicationExecutor] conducting a dry run election to see if we could be elected 2019-05-06T16:51:11.766+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Connecting to 172.30.129.78:30071 2019-05-06T16:51:11.767+0800 I REPL [ReplicationExecutor] VoteRequester(term 3 dry run) received a yes vote from 172.30.129.7:30071; response message: { term: 3, voteGranted: true, reason: "", ok: 1.0 } 2019-05-06T16:51:11.767+0800 I REPL [ReplicationExecutor] dry election run succeeded, running for election 2019-05-06T16:51:11.768+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Connecting to 172.30.129.78:30071 2019-05-06T16:51:11.771+0800 I REPL [ReplicationExecutor] VoteRequester(term 4) received a yes vote from 172.30.129.7:30071; response message: { term: 4, voteGranted: true, reason: "", ok: 1.0 } 2019-05-06T16:51:11.771+0800 I REPL [ReplicationExecutor] election succeeded, assuming primary role in term 4 2019-05-06T16:51:11.771+0800 I REPL [ReplicationExecutor] transition to PRIMARY 2019-05-06T16:51:11.771+0800 I REPL [ReplicationExecutor] Entering primary catch-up mode. 2019-05-06T16:51:11.771+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Ending connection to host 172.30.129.78:30071 due to bad connection status; 2 connections to that host remain open 2019-05-06T16:51:11.771+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Connecting to 172.30.129.78:30071 2019-05-06T16:51:13.350+0800 I REPL [ReplicationExecutor] Error in heartbeat request to 172.30.129.78:30071; ExceededTimeLimit: Couldn't get a connection within the time limit

備節點2的日志

2019-05-06T16:51:12.816+0800 I ASIO [NetworkInterfaceASIO-Replication-0] Ending connection to host 172.30.129.78:30071 due to bad connection status; 0 connections to that host remain open 2019-05-06T16:51:12.816+0800 I REPL [ReplicationExecutor] Error in heartbeat request to 172.30.129.78:30071; ExceededTimeLimit: Operation timed out, request was RemoteCommand 72553 -- target:172.30.129.78:30071 db:admin expDate:2019-05-06T16:51:12.816+0800 cmd:{ replSetHeartbeat: "shard0", configVersion: 96911, from: "172.30.129.7:30071", fromId: 1, term: 3 } 2019-05-06T16:51:12.821+0800 I REPL [ReplicationExecutor] Member 172.30.129.160:30071 is now in state PRIMARY

可以看到,備節點1在 16:51:11 時主動發起了選舉,并成為了新的主節點,隨即備節點2在 16:51:12  獲知了最新的主節點信息,因此可以確認此時主備切換已經完成。

同時在日志中出現的,還有對于原主節點(172.30.129.78:30071)大量心跳失敗的信息。

那么,備節點具體是怎么感知到主節點已經 Down 掉的,主備節點之間的心跳是如何運作的,這對數據的同步復制又有什么影響?

下面,我們挖掘一下 ** 副本集的 自動故障轉移(Failover)** 機制

副本集 如何實現 Failover

如下是一個PSS(一主兩備)架構的副本集,主節點除了與兩個備節點執行數據復制之外,三個節點之間還會通過心跳感知彼此的存活。

MongoDB一次節點宕機引發的思考是怎樣的

一旦主節點發生故障以后,備節點將在某個周期內檢測到主節點處于不可達的狀態,此后將由其中一個備節點事先發起選舉并最終成為新的主節點。 這個檢測周期  由electionTimeoutMillis 參數確定,默認是10s。

MongoDB一次節點宕機引發的思考是怎樣的

接下來,我們通過一些源碼看看該機制是如何實現的:

<<來自 MongoDB 3.4源碼>>

db/repl/replication_coordinator_impl_heartbeat.cpp

相關方法

  • ReplicationCoordinatorImpl::_startHeartbeats_inlock 啟動各成員的心跳

  • ReplicationCoordinatorImpl::_scheduleHeartbeatToTarget 調度任務-(計劃)向成員發起心跳

  • ReplicationCoordinatorImpl::_doMemberHeartbeat 執行向成員發起心跳

  • ReplicationCoordinatorImpl::_handleHeartbeatResponse 處理心跳響應

  • ReplicationCoordinatorImpl::_scheduleNextLivenessUpdate_inlock  調度保活狀態檢查定時器

  • ReplicationCoordinatorImpl::_cancelAndRescheduleElectionTimeout_inlock  取消并重新調度選舉超時定時器

  • ReplicationCoordinatorImpl::_startElectSelfIfEligibleV1 發起主動選舉

db/repl/topology_coordinator_impl.cpp

相關方法

  • TopologyCoordinatorImpl::prepareHeartbeatRequestV1 構造心跳請求數據

  • TopologyCoordinatorImpl::processHeartbeatResponse 處理心跳響應并構造下一步Action實例

下面這個圖,描述了各個方法之間的調用關系

MongoDB一次節點宕機引發的思考是怎樣的

圖-主要關系

心跳的實現

首先,在副本集組建完成之后,節點會通過ReplicationCoordinatorImpl::_startHeartbeats_inlock方法開始向其他成員發送心跳:

void ReplicationCoordinatorImpl::_startHeartbeats_inlock() {  const Date_t now = _replExecutor.now();  _seedList.clear();  //獲取副本集成員  for (int i = 0; i < _rsConfig.getNumMembers(); ++i) {  if (i == _selfIndex) {  continue;  }  //向其他成員發送心跳  _scheduleHeartbeatToTarget(_rsConfig.getMemberAt(i).getHostAndPort(), i, now);  }  //僅僅是刷新本地的心跳狀態數據  _topCoord->restartHeartbeats();  //使用V1的選舉協議(3.2之后)  if (isV1ElectionProtocol()) {  for (auto&& slaveInfo : _slaveInfo) {  slaveInfo.lastUpdate = _replExecutor.now();  slaveInfo.down = false;  }  //調度保活狀態檢查定時器  _scheduleNextLivenessUpdate_inlock();  } }

在獲得當前副本集的節點信息后,調用_scheduleHeartbeatToTarget方法對其他成員發送心跳,

這里_scheduleHeartbeatToTarget 的實現比較簡單,其真正發起心跳是由 _doMemberHeartbeat 實現的,如下:

void ReplicationCoordinatorImpl::_scheduleHeartbeatToTarget(const HostAndPort& target,  int targetIndex,  Date_t when) {  //執行調度,在某個時間點調用_doMemberHeartbeat  _trackHeartbeatHandle(  _replExecutor.scheduleWorkAt(when,  stdx::bind(&ReplicationCoordinatorImpl::_doMemberHeartbeat,  this,  stdx::placeholders::_1,  target,  targetIndex))); }

ReplicationCoordinatorImpl::_doMemberHeartbeat 方法的實現如下:

void ReplicationCoordinatorImpl::_doMemberHeartbeat(ReplicationExecutor::CallbackArgs cbData,  const HostAndPort& target,  int targetIndex) {  LockGuard topoLock(_topoMutex);  //取消callback 跟蹤  _untrackHeartbeatHandle(cbData.myHandle);  if (cbData.status == ErrorCodes::CallbackCanceled) {  return;  }  const Date_t now = _replExecutor.now();  BSONObj heartbeatObj;  Milliseconds timeout(0);  //3.2 以后的版本  if (isV1ElectionProtocol()) {  const std::pair<ReplSetHeartbeatArgsV1, Milliseconds> hbRequest =  _topCoord->prepareHeartbeatRequestV1(now, _settings.ourSetName(), target);  //構造請求,設置一個timeout  heartbeatObj = hbRequest.first.toBSON();  timeout = hbRequest.second;  } else {  ...  }  //構造遠程命令  const RemoteCommandRequest request(  target, "admin", heartbeatObj, BSON(rpc::kReplSetMetadataFieldName << 1), nullptr, timeout);  //設置遠程命令回調,指向_handleHeartbeatResponse方法  const ReplicationExecutor::RemoteCommandCallbackFn callback =  stdx::bind(&ReplicationCoordinatorImpl::_handleHeartbeatResponse,  this,  stdx::placeholders::_1,  targetIndex);  _trackHeartbeatHandle(_replExecutor.scheduleRemoteCommand(request, callback)); }

上面的代碼中存在的一些細節:

  • 心跳的超時時間,在_topCoord.prepareHeartbeatRequestV1方法中就已經設定好了

  • 具體的算法就是:

**hbTimeout=_rsConfig.getHeartbeatTimeoutPeriodMillis() - alreadyElapsed**

其中heartbeatTimeoutPeriodMillis是可配置的參數,默認是10s,  那么alreadyElapsed是指此前連續心跳失敗(最多2次)累計的消耗時間,在心跳成功響應或者超過10s后alreadyElapsed會置為0。因此可以判斷,隨著心跳失敗次數的增加,超時時間會越來越短(心跳更加密集)

心跳執行的回調,指向自身的_handleHeartbeatResponse方法,該函數實現了心跳響應成功、失敗(或是超時)之后的流程處理。

ReplicationCoordinatorImpl::_handleHeartbeatResponse方法的代碼片段:

void ReplicationCoordinatorImpl::_handleHeartbeatResponse(  const ReplicationExecutor::RemoteCommandCallbackArgs& cbData, int targetIndex) {  LockGuard topoLock(_topoMutex);  // remove handle from queued heartbeats  _untrackHeartbeatHandle(cbData.myHandle);  ...  //響應成功后  if (responseStatus.isOK()) {  networkTime = cbData.response.elapsedMillis.value_or(Milliseconds{0});  const auto& hbResponse = hbStatusResponse.getValue();  // 只要primary 心跳響應成功,就會重新調度 electionTimeout定時器  if (hbResponse.hasState() && hbResponse.getState().primary() &&  hbResponse.getTerm() == _topCoord->getTerm()) {  //取消并重新調度 electionTimeout定時器  cancelAndRescheduleElectionTimeout();  }  }  ...  //調用topCoord的processHeartbeatResponse方法處理心跳響應狀態,并返回下一步執行的Action  HeartbeatResponseAction action = _topCoord->processHeartbeatResponse(  now, networkTime, target, hbStatusResponse, lastApplied);  ...  //調度下一次心跳,時間間隔采用action提供的信息  _scheduleHeartbeatToTarget(  target, targetIndex, std::max(now, action.getNextHeartbeatStartDate()));  //根據Action 執行處理  _handleHeartbeatResponseAction(action, hbStatusResponse, false); }

這里省略了許多細節,但仍然可以看到,在響應心跳時會包含這些事情的處理:

對于主節點的成功響應,會重新調度 electionTimeout定時器(取消之前的調度并重新發起)

通過_topCoord對象的processHeartbeatResponse方法解析處理心跳響應,并返回下一步的Action指示

根據Action 指示中的下一次心跳時間設置下一次心跳定時任務

處理Action指示的動作

那么,心跳響應之后會等待多久繼續下一次心跳呢? 在  TopologyCoordinatorImpl::processHeartbeatResponse方法中,實現邏輯為:

如果心跳響應成功,會等待heartbeatInterval,該值是一個可配參數,默認為2s;

如果心跳響應失敗,則會直接發送心跳(不等待)。

代碼如下:

HeartbeatResponseAction TopologyCoordinatorImpl::processHeartbeatResponse(...) {    ...  const Milliseconds alreadyElapsed = now - hbStats.getLastHeartbeatStartDate();  Date_t nextHeartbeatStartDate;  // 計算下一次 心跳啟動時間  // numFailuresSinceLastStart 對應連續失敗的次數(2次以內)  if (hbStats.getNumFailuresSinceLastStart() <= kMaxHeartbeatRetries &&  alreadyElapsed < _rsConfig.getHeartbeatTimeoutPeriod()) {  // 心跳失敗,不等待,直接重試心跳  nextHeartbeatStartDate = now;  } else {  // 心跳成功,等待一定間隔后再次發送(一般是2s)  nextHeartbeatStartDate = now + heartbeatInterval;  }  ...  // 決定下一步的動作,可能發生 tack over(本備節點優先級更高,且數據與主節點一樣新時)  HeartbeatResponseAction nextAction;  if (_rsConfig.getProtocolVersion() == 0) {  ...  } else {  nextAction = _updatePrimaryFromHBDataV1(memberIndex, originalState, now, myLastOpApplied);  }  nextAction.setNextHeartbeatStartDate(nextHeartbeatStartDate);  return nextAction; }

electionTimeout 定時器

至此,我們已經知道了心跳實現的一些細節,默認情況下副本集節點會每2s向其他節點發出心跳(默認的超時時間是10s)。

如果心跳成功,將會持續以2s的頻率繼續發送心跳,在心跳失敗的情況下,則會立即重試心跳(以更短的超時時間),一直到心跳恢復成功或者超過10s的周期。

那么,心跳失敗是如何觸發主備切換的呢,electionTimeout 又是如何發揮作用?

在前面的過程中,與electionTimeout參數相關兩個方法如下,它們也分別對應了單獨的定時器:

ReplicationCoordinatorImpl::_scheduleNextLivenessUpdate_inlock  發起保活狀態檢查定時器

ReplicationCoordinatorImpl::_cancelAndRescheduleElectionTimeout_inlock  重新發起選舉超時定時器

第一個是 _scheduleNextLivenessUpdate_inlock這個函數,它的作用在于保活狀態檢測,如下:

void ReplicationCoordinatorImpl::_scheduleNextLivenessUpdate_inlock() {  //僅僅支持3.2+  if (!isV1ElectionProtocol()) {  return;  }    // earliestDate 取所有節點中更新時間最早的(以盡可能早的發現問題)  // electionTimeoutPeriod 默認為 10s  auto nextTimeout = earliestDate + _rsConfig.getElectionTimeoutPeriod();    // 設置超時回調函數為 _handleLivenessTimeout  auto cbh = _scheduleWorkAt(nextTimeout,  stdx::bind(&ReplicationCoordinatorImpl::_handleLivenessTimeout,  this,  stdx::placeholders::_1)); }

因此,在大約10s后,如果沒有什么意外,_handleLivenessTimeout將會被觸發,如下:

void ReplicationCoordinatorImpl::_handleLivenessTimeout(...) {  ...  for (auto&& slaveInfo : _slaveInfo) {  ...  //lastUpdate 不夠新(小于electionTimeout)  if (now - slaveInfo.lastUpdate >= _rsConfig.getElectionTimeoutPeriod()) {  ...  //在保活周期后仍然未更新節點,置為down狀態  slaveInfo.down = true;  //如果當前節點是主,且檢測到某個備節點為down的狀態,進入memberdown流程  if (_memberState.primary()) {    //調用_topCoord的setMemberAsDown方法,記錄某個備節點不可達,并獲得下一步的指示  //當大多數節點不可見時,這里會獲得讓自身降備的指示  HeartbeatResponseAction action =  _topCoord->setMemberAsDown(now, memberIndex, _getMyLastDurableOpTime_inlock());  //執行指示  _handleHeartbeatResponseAction(action,  makeStatusWith<ReplSetHeartbeatResponse>(),  true);  }  }  }  //繼續調度下一個周期  _scheduleNextLivenessUpdate_inlock(); }

可以看到,這個定時器主要是用于實現主節點對其他節點的保活探測邏輯:

當主節點發現大多數節點不可達時(不滿足大多數原則),將會讓自己執行降備

因此,在一個三節點的副本集中,其中兩個備節點掛掉后,主節點會自動降備。 這樣的設計主要是為了避免產生意外的數據不一致情況產生。

MongoDB一次節點宕機引發的思考是怎樣的

圖- 主自動降備

第二個是_cancelAndRescheduleElectionTimeout_inlock函數,這里則是實現自動Failover的關鍵了,

它的邏輯中包含了一個選舉定時器,代碼如下:

void ReplicationCoordinatorImpl::_cancelAndRescheduleElectionTimeout_inlock() {  //如果上一個定時器已經啟用了,則直接取消  if (_handleElectionTimeoutCbh.isValid()) {  LOG(4) << "Canceling election timeout callback at " << _handleElectionTimeoutWhen;  _replExecutor.cancel(_handleElectionTimeoutCbh);  _handleElectionTimeoutCbh = CallbackHandle();  _handleElectionTimeoutWhen = Date_t();  }  //僅支持3.2后的V1版本  if (!isV1ElectionProtocol()) {  return;  }  //僅備節點可執行  if (!_memberState.secondary()) {  return;  }  ...  //是否可以選舉  if (!_rsConfig.getMemberAt(_selfIndex).isElectable()) {  return;  }  //檢測周期,由 electionTimeout + randomOffset  //randomOffset是隨機偏移量,默認為 0~0.15*ElectionTimeoutPeriod = 0~1.5s  Milliseconds randomOffset = _getRandomizedElectionOffset();  auto now = _replExecutor.now();  auto when = now + _rsConfig.getElectionTimeoutPeriod() + randomOffset;    LOG(4) << "Scheduling election timeout callback at " << when;  _handleElectionTimeoutWhen = when;  //觸發調度,時間為 now + ElectionTimeoutPeriod + randomOffset  _handleElectionTimeoutCbh =  _scheduleWorkAt(when,  stdx::bind(&ReplicationCoordinatorImpl::_startElectSelfIfEligibleV1,  this,  StartElectionV1Reason::kElectionTimeout)); }

上面代碼展示了這個選舉定時器的邏輯,在每一個檢測周期中,定時器都會嘗試執行超時回調,

而回調函數指向的是_startElectSelfIfEligibleV1,這里面就實現了主動發起選舉的功能,

如果心跳響應成功,通過cancelAndRescheduleElectionTimeout調用將直接取消當次的超時回調(即不會發起選舉)

如果心跳響應遲遲不能成功,那么定時器將被觸發,進而導致備節點發起選舉并成為新的主節點!

同時,這個回調方法(產生選舉)被觸發必須要滿足以下條件:

  1. 鴻蒙官方戰略合作共建——HarmonyOS技術社區

  2. 當前是備節點

  3. 當前節點具備選舉權限

  4. 在檢測周期內仍然沒有與主節點心跳成功

這其中的檢測周期略大于electionTimeout(10s),加入一個隨機偏移量后大約是10-11.5s內,猜測這樣的設計是為了錯開多個備節點主動選舉的時間,提升成功率。

最后,將整個自動選舉切換的邏輯梳理后,如下圖所示:

MongoDB一次節點宕機引發的思考是怎樣的

圖-超時自動選舉

業務影響評估

副本集發生主備切換的情況下,不會影響現有的讀操作,只會影響寫操作。 如果使用3.6及以上版本的驅動,可以通過開啟retryWrite來降低影響。

但是如果主節點是屬于強制掉電,那么整個 Failover  過程將會變長,很可能需要在Election定時器超時后才被副本集感知并恢復,這個時間窗口會在12s以內。

此外還需要考慮客戶端或mongos對于副本集角色的監視和感知行為。但總之在問題恢復之前,對于原主節點的任何讀寫都會發生超時。

因此,對于極為重要的業務,建議最好在業務層面做一些防護策略,比如設計重試機制。

上述內容就是MongoDB一次節點宕機引發的思考是怎樣的,你們學到知識或技能了嗎?如果還想學到更多技能或者豐富自己的知識儲備,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

锦州市| 泽库县| 游戏| 英超| 上犹县| 利川市| 乌审旗| 临沂市| 浦城县| 团风县| 青川县| 佛冈县| 阳曲县| 山东省| 武清区| 都江堰市| 靖安县| 深圳市| 屏边| 颍上县| 合山市| 九龙城区| 咸宁市| 龙胜| 勐海县| 北川| 宝应县| 佛山市| 仁寿县| 天长市| 辉县市| 蕉岭县| 甘肃省| 奈曼旗| 开鲁县| 陆良县| 腾冲县| 东乌珠穆沁旗| 定兴县| 炉霍县| 灵石县|