全部產品
Search
文件中心

ApsaraDB for MongoDB:Opcounters指標和Repl Opcounters指標

更新時間:Dec 24, 2024

ApsaraDB for MongoDB提供了Opcounters和Repl Opcounters相關的指標,本文將為您介紹Opcounters指標和Repl Opcounters指標以及相關的常見問題。

Opcounters指標和Repl Opcounters指標支援的執行個體類型,請參見監控項說明

Opcounters指標

ApsaraDB for MongoDB的監控資訊和效能趨勢功能中均提供了Opcounters指標的相關參數:

Opcounters指標由insert、query、update、delete、getmore和command幾個操作類型組成,該指標展示了mongod或mongos自上次啟動以來啟動並執行不同資料庫操作類型的數量統計。

指標

單位

說明

insert

個/每秒

MongoDB每秒插入操作的數量。

query

個/每秒

MongoDB每秒查詢操作的數量。

update

個/每秒

MongoDB每秒更新操作的數量。

delete

個/每秒

MongoDB每秒刪除操作的數量。

getmore

個/每秒

MongoDB每秒擷取更多操作的數量。

command

個/每秒

MongoDB每秒命令操作的數量。

Opcounters除了會記錄用戶端側發起的操作外,同時也會記錄一些資料庫內部的操作。

指標

說明

insert

  • Chunk遷移過程中,作為接收方分區的主節點將會記錄這些插入操作。

  • 複本集中的成員每隔5分鐘(該時間可通過logicalSessionRefreshMillis參數進行調整)會將記憶體中的會話持久化到config.system.sessions集合中,此時會產生一些插入操作。

query

  • 如果開啟了鏡像讀功能(MongoDB 4.4以上版本預設開啟),從節點上也會有一定的查詢操作。

  • MongoDB的changeStream如果設定為fullDocument: "updateLookup",也會產生額外的查詢操作。

update

insert操作類型類似,當重新整理記憶體會話時,相應的update操作也會被記錄。

delete

  • TTL索引帶來的刪除操作不會被記錄

  • Chunk遷移後,刪除孤立文檔的操作不會被記錄

getmore

主從同步時,資料庫對local.oplog.rs集合的getMore操作也會被記錄。

command

  • 記錄除了insertupdatedelete這些寫命令以及querygetmore以外的所有命令。

  • 記錄內部探活使用的isMasterhello命令。

  • 記錄主從同步使用的replSetUpdatePosition等命令。

  • 記錄監控使用的serverStatuslistCollectionscollStatsreplSetGetStatus等命令。

更多命令請參見Command

Repl Opcounters指標

ApsaraDB for MongoDB監控資訊中提供了Repl Opcounters指標的相關參數,通常情況下,你僅需要關注從節點上的Repl Opcounters指標。

Repl Opcounters-cn.png

您也可以在從節點上查看Opcounters指標,該指標主要由以下兩個部分組成:

  • 在從節點上執行的用戶端查詢操作(通過指定相應的readPreference)。

  • 在local庫執行的所有寫入操作(通過主從同步獲得的操作)。

與Opcounters指標類似,Repl Opcounters指標也由insert、query、update、delete、getMore和Command幾個操作類型組成,該指標展示了mongod自上次啟動以來按不同類型匯總的資料庫複寫操作。

指標

單位

說明

insert

個/每秒

MongoDB每秒插入操作的數量。

query

個/每秒

MongoDB每秒查詢操作的數量。

update

個/每秒

MongoDB每秒更新操作的數量。

delete

個/每秒

MongoDB每秒刪除操作的數量。

getmore

個/每秒

MongoDB每秒擷取更多操作的數量。

command

個/每秒

MongoDB每秒命令操作的數量。

Repl Opcounters指標會計算應用在從節點上的所有寫操作,除了Opcounters指標中提及的操作外,還包括以下操作:

  • 會話(session)重新整理觸發的insert和update操作。

  • TTL索引帶來的刪除操作。

  • 刪除孤立文檔觸發的刪除操作(通常延遲發生在Chunk遷移後)。

  • 寫入系統集合的相關操作,例如可重試寫入(Retryable Writes)會有對config.transactions的寫入操作。

由於MongoDB在主從複製過程中序列化操作的方式不同,因此,從節點上的Repl Opcounters指標值與主節點上的Opcounters指標值不一樣也是符合預期的。

常見問題

為什麼從節點的Repl Opcounters指標值比主節點的Opcounters指標值高很多?

影響多個文檔的操作(如批量插入、多次更新或多次刪除操作)會被解釋為單個操作。當影響多個文檔的操作複製到從節點時,由於主從複製是基於每個文檔進行的,從節點上的相關Repl Opcounters指標值可能大於在主節點上觀察到的Opcounters指標值。

樣本如下:

  1. 更新前檢查Opcounters計數器。

    • 主節點的Opcounters計數器數量顯示為13。

      > db.serverStatus().opcounters.update
      NumberLong(13)
    • 從節點的Repl Opcounters計數器數量顯示為11。

      > db.serverStatus().opcountersRepl.update
      NumberLong(11)
  2. 主節點執行一次批次更新。通過返回的modifiedCount欄位可以看到本次批次更新修改了4條文檔。

    > db.coll.updateMany({x:2},{$set:{x:3}})
    { "acknowledged" : true, "matchedCount" : 4, "modifiedCount" : 4 }
  3. 更新後再次檢查Opcounters計數器。

    • 主節點的Opcounters計數器數量顯示為14。

      > db.serverStatus().opcounters.update
      NumberLong(14)
    • 從節點的Repl Opcounters計數器數量顯示為15。

      > db.serverStatus().opcountersRepl.update
      NumberLong(15)

為什麼執行的都是update操作,但從節點的Repl Opcounters指標中卻顯示了不少insert類操作?

可能是因為您的update操作指定了{upsert:true}選項,當期望update的文檔不存在時,會轉為insert操作執行。當oplog裡記錄的是insert操作時,通過主從複製同步到從節點的也將是insert操作,因此Repl Opcounters裡記錄的也就是相應insert操作。

樣本如下:

  1. 更新前檢查Opcounters計數器。

    • 主節點的Opcounters計數器中,update數量顯示為33;insert數量顯示為1516。

      > db.serverStatus().opcounters
      {
        "insert" : NumberLong(1516),
        "query" : NumberLong(70),
        "update" : NumberLong(33),
        "delete" : NumberLong(1043),
        "getmore" : NumberLong(2662),
        "command" : NumberLong(4000)
      }
    • 從節點的Repl Opcounters計數器,update數量顯示為24;insert數量顯示為1539。

      > db.serverStatus().opcountersRepl
      {
        "insert" : NumberLong(1539),
        "query" : NumberLong(0),
        "update" : NumberLong(24),
        "delete" : NumberLong(6),
        "getmore" : NumberLong(0),
        "command" : NumberLong(26)
      }
  2. 在主節點上執行帶{upsert:true}選項的update操作,通過返回的upsertedId欄位可以看到成功插入了1條文檔。

    > db.coll.updateOne({x:"a"}, {$set:{x:"b"}}, {upsert:true})
    {
      "acknowledged" : true,
      "matchedCount" : 0,
      "modifiedCount" : 0,
      "upsertedId" : ObjectId("64bf72b829907f52b4b363ea")
    }
  3. 更新後再次檢查Opcounters計數器。

    • 主節點的Opcounters計數器中,update數量顯示為34;insert數量依舊顯示為1516。

      > db.serverStatus().opcounters
      {
        "insert" : NumberLong(1516),
        "query" : NumberLong(70),
        "update" : NumberLong(34),   // 注意這裡計數器的變化
        "delete" : NumberLong(1043),
        "getmore" : NumberLong(2706),
        "command" : NumberLong(4286)
      }
    • 從節點的Repl Opcounters計數器,update數量依舊顯示為24;insert數量顯示為1540。

      > db.serverStatus().opcountersRepl
      {
        "insert" : NumberLong(1540), // 注意這裡計數器的變化
        "query" : NumberLong(0),
        "update" : NumberLong(24),
        "delete" : NumberLong(6),
        "getmore" : NumberLong(0),
        "command" : NumberLong(26)
      }

為什麼主節點上的update操作那麼多,但從節點複製的update操作那麼少?

可能是因為您的商務邏輯裡包含了重複的update操作。重複的update操作由於並沒有發生實際的改動,並不會被複製到從節點上。因此從節點上看到的update運算元量會更少。

樣本如下:

  1. 更新前檢查Opcounters計數器。

    • 主節點的Opcounters計數器中update數量顯示為34。

      > db.serverStatus().opcounters
      {
        "insert" : NumberLong(1516),
        "query" : NumberLong(70),
        "update" : NumberLong(34),
        "delete" : NumberLong(1043),
        "getmore" : NumberLong(2760),
        "command" : NumberLong(4609)
      }
    • 從節點的Repl Opcounters計數器中update數量顯示為24。

      > db.serverStatus().opcountersRepl
      {
        "insert" : NumberLong(1540),
        "query" : NumberLong(0),
        "update" : NumberLong(24),
        "delete" : NumberLong(6),
        "getmore" : NumberLong(0),
        "command" : NumberLong(26)
      }
  2. 在主節點上執行update操作,通過返回資訊可以看到並未發生實際修改。

    > db.coll.updateMany({x:"ab"},{$set:{x:"cd"}})
    { "acknowledged" : true, "matchedCount" : 0, "modifiedCount" : 0 }
  3. 更新後再次檢查Opcounters計數器。

    • 主節點的Opcounters計數器中update數量顯示為35。

      > db.serverStatus().opcounters
      {
        "insert" : NumberLong(1516),
        "query" : NumberLong(70),
        "update" : NumberLong(35),    // 注意這裡計數器的變化
        "delete" : NumberLong(1043),
        "getmore" : NumberLong(2778),
        "command" : NumberLong(4729)
      }
    • 從節點的Repl Opcounters計數器中update數量依舊顯示為24。

      > db.serverStatus().opcountersRepl
      {
        "insert" : NumberLong(1540),
        "query" : NumberLong(0),
        "update" : NumberLong(24),
        "delete" : NumberLong(6),
        "getmore" : NumberLong(0),
        "command" : NumberLong(26)
      }

為什麼沒有使用資料庫,但監控上依然能看到一些Opcounters各種類型的操作?

無業務訪問情況下,監控上呈現的少量操作主要由以下幾部分組成:

  • 為了維持MongoDB資料庫正常運轉的一些內部基本操作,比如複本集心跳、主從同步、會話重新整理等。

  • ApsaraDB for MongoDB資料庫周邊管控組件的一些日常操作,比如探活、監控、監聽等。

為什麼Opcounters裡的指標和執行個體oplog裡按op彙總得到的結果不一樣?

在oplog中,您可以通過op欄位來區分具體的操作類型。常見的取值範圍如下:

kCommand: "c"
kInsert: "i"
kUpdate: "u"
kDelete: "d"
kNoop: "n"

事務都是op:c的操作類型,僅在內部的o.applyOps中儲存了事務內包含的所有insertupdatedelete操作。如果您僅按oplog的op欄位來進行彙總分析的話,在有事務的情境中,得出的結果會和Opcounters中顯示的結果不一致。更多關於oplog格式的介紹,請參見oplog各欄位的解析

如您期望統計事務內的具體操作類型數量,可以通過mongo shell用戶端串連MongoDB後執行如下命令。

use local
db.oplog.rs.aggregate([{$match:{"op":"c","ts":{"$gte": Timestamp(1733849400,0)},"o.applyOps":{$exists:true},"o.applyOps.0.op":"u"}},{$count:"count"}])

參數說明如下:

  • Timestamp(1733849400,0):一個查詢oplog格式中的ts時間戳記的下邊界,您需要替換為期望查詢的時間。該時間戳記您可以從local.oplog.rs裡讀取或直接使用UNIX時間戳記。

  • "o.applyOps.0.op":"u":事務oplog中的第一條操作為update,您可以替換為其他動作類型,例如第一條操作為insert時,您可以替換為"o.applyOps.0.op":"i"

  • {$count:"count"}:僅統計符合要求的oplog條數,您可以使用彙總語句的其他動作符來進行其他分析。

如果您需要查看事務相關的監控指標,可以在ApsaraDB for MongoDB控制台的監控資訊中查看事務運算元指標,具體指標,請參見監控項說明

樣本如下:

  1. 更新前檢查Opcounters計數器。

    > db.serverStatus().opcounters
    {
      "insert" : NumberLong(4), 
      "query" : NumberLong(6723),
      "update" : NumberLong(110489),
      "delete" : NumberLong(3065),
      "getmore" : NumberLong(222670),
      "command" : NumberLong(1917525)
    }
  2. 在主節點上執行一個僅事務內的寫操作。

    // 開啟一個會話
    session = db.getMongo().startSession( { readPreference: { mode: "primary" } } );
    coll1 = session.getDatabase("mydb1").foo;
    coll2 = session.getDatabase("mydb2").bar;
    // 開啟一個事務
    session.startTransaction( { readConcern: { level: "local" }, writeConcern: { w: "majority" } } );
    // 在事務中執行2個insert操作
    try {
       coll1.insertOne( { abc: 1 } );
       coll2.insertOne( { xyz: 999 } );
    } catch (error) {
       // 遇到問題時中止事務
       session.abortTransaction();
       throw error;
    }
    // 提交事務
    session.commitTransaction();
    session.endSession();
  3. 更新後再次檢查Opcounters計數器。Opcounters計數器中insert數量由4變成了6。

    > db.serverStatus().opcounters
    {
      "insert" : NumberLong(6),   // 注意這裡計數器的變化
      "query" : NumberLong(6728),
      "update" : NumberLong(110532),
      "delete" : NumberLong(3067),
      "getmore" : NumberLong(222823),
      "command" : NumberLong(1918887)
    }