Opcounters指标和Repl Opcounters指标

云数据库 MongoDB 版提供了OpcountersRepl Opcounters相关的指标,本文将为您介绍Opcounters指标和Repl Opcounters指标以及相关的常见问题。

Opcounters指标和Repl Opcounters指标支持的实例类型,请参见监控项说明

Opcounters指标

云数据库 MongoDB 版的监控信息和性能趋势功能中均提供了Opcounters指标的相关参数:

Opcounters指标由insert、query、update、delete、getmorecommand几个操作类型组成,该指标展示了mongodmongos自上次启动以来运行的不同数据库操作类型的数量统计。

指标

单位

说明

insert

个/每秒

MongoDB每秒插入操作的数量。

query

个/每秒

MongoDB每秒查询操作的数量。

update

个/每秒

MongoDB每秒更新操作的数量。

delete

个/每秒

MongoDB每秒删除操作的数量。

getmore

个/每秒

MongoDB每秒获取更多操作的数量。

command

个/每秒

MongoDB每秒命令操作的数量。

Opcounters除了会记录客户端侧发起的操作外,同时也会记录一些数据库内部的操作。

指标

说明

insert

  • Chunk迁移过程中,作为接收方分片的主节点将会记录这些插入操作。

  • 副本集中的成员每隔5分钟(该时间可通过logicalSessionRefreshMillis参数进行调整)会将内存中的会话持久化到config.system.sessions集合中,此时会产生一些插入操作。

query

  • 如果开启了镜像读功能(MongoDB 4.4以上版本默认开启),从节点上也会有一定的查询操作。

  • MongoDBchangeStream如果设置为fullDocument: "updateLookup",也会产生额外的查询操作。

update

insert操作类型类似,当刷新内存会话时,相应的update操作也会被记录。

delete

  • TTL索引带来的删除操作不会被记录

  • Chunk迁移后,删除孤立文档的操作不会被记录

getmore

主从同步时,数据库对local.oplog.rs集合的getMore操作也会被记录。

command

  • 记录除了insertupdatedelete这些写命令以及querygetmore以外的所有命令。

  • 记录内部探活使用的isMasterhello命令。

  • 记录主从同步使用的replSetUpdatePosition等命令。

  • 记录监控使用的serverStatuslistCollectionscollStatsreplSetGetStatus等命令。

更多命令请参见Command

Repl Opcounters指标

云数据库 MongoDB 版监控信息中提供了Repl Opcounters指标的相关参数,通常情况下,你仅需要关注从节点上的Repl Opcounters指标。

Repl Opcounters-cn.png

您也可以在从节点上查看Opcounters指标,该指标主要由以下两个部分组成:

  • 在从节点上执行的客户端查询操作(通过指定相应的readPreference)。

  • local库执行的所有写入操作(通过主从同步获得的操作)。

Opcounters指标类似,Repl Opcounters指标也由insert、query、update、delete、getMoreCommand几个操作类型组成,该指标展示了mongod自上次启动以来按不同类型汇总的数据库复制操作。

指标

单位

说明

insert

个/每秒

MongoDB每秒插入操作的数量。

query

个/每秒

MongoDB每秒查询操作的数量。

update

个/每秒

MongoDB每秒更新操作的数量。

delete

个/每秒

MongoDB每秒删除操作的数量。

getmore

个/每秒

MongoDB每秒获取更多操作的数量。

command

个/每秒

MongoDB每秒命令操作的数量。

Repl Opcounters指标会计算应用在从节点上的所有写操作,除了Opcounters指标中提及的操作外,还包括以下操作:

  • 会话(session)刷新触发的insertupdate操作。

  • 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数据库正常运转的一些内部基本操作,比如副本集心跳、主从同步、会话刷新等。

  • 云数据库 MongoDB 版数据库周边管控组件的一些日常操作,比如探活、监控、监听等。

为什么Opcounters里的指标和实例oplog里按op聚合得到的结果不一样?

oplog中,您可以通过op字段来区分具体的操作类型。常见的取值范围如下:

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

事务都是op:c的操作类型,仅在内部的o.applyOps中存储了事务内包含的所有insertupdatedelete操作。如果您仅按oplogop字段来进行聚合分析的话,在有事务的场景中,得出的结果会和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条数,您可以使用聚合语句的其他操作符来进行其他分析。

如果您需要查看事务相关的监控指标,可以在云数据库 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" } } );
    // 在事务中执行2insert操作
    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)
    }