MongoDB WriteConflict 排查思路

MongoDB WriteConflict 排查思路

业务中时长遇到 WriteConflict 错误,虽然业务上有重试最终没有影响。但是理清逻辑,确认 到底是哪些地方导致的还是非常重要。下面我们提供一下事后常见的排查路径,希望对大家有用。

在 MongoDB 中,事务冲突的详细信息通常会在服务器日志中记录,但记录的详细程度取决于MongoDB的日志级别设置。

怎么知道发生了 WriteConflict

通常有 2 个角度可以观察到,一个是 MongoDB 本身的日志、另外就是业务应用在通过 MongoDB 驱动执行操作时候的报错。

查询 MongoDB 日志

我们可以在 MongoDB 的日志中查找到事务冲突的信息。以下日志路径仅供参考,具体的路径要根据自己的配置文件进行调整。

bash 复制代码
   grep "WriteConflict" /var/log/mongodb/mongod.log
   grep "TransientTransactionError" /var/log/mongodb/mongod.log

如果希望增加日志详细度(如果需要更多信息)

  • 临时提高日志级别,特别是事务相关操作:
javascript 复制代码
db.setLogLevel(1, "command")
db.setLogLevel(1, "transaction")
  • 记录后别忘了恢复默认设置:
javascript 复制代码
db.setLogLevel(-1, "command")
db.setLogLevel(-1, "transaction")

tracing

如果你的业务有 tracing 系统,那么通常我们可以通过 tracing 获取到相关信息(业务应用记录的)。

日志中的信息限制

MongoDB 日志通常不会显示具体的冲突事务详情:

  • 不会直接标识哪两个事务发生了冲突,比如我们某一个操作出错了,他只会告诉你当前的操作发生了事务冲突,和一些具体的时间信息。无法知道另外一个事务 ID 是啥。
  • 不会完整列出冲突事务的操作内容
  • 主要提供时间戳和冲突类型

通过 oplog 查询可能的事务操作

有一种思路,就是我们确认时间范围之后,可以查询 oplog 来定位是和哪个事务冲突了。

这需要注意在 MongoDB 分片集群模式下,通过 mongos 连接的时候无法访问到 local 数据库或 oplog.rs 集合。因为 local 数据库仅存在于各个独立的 shard 和 config server 上。

使用 oplog 查找相关信息

  1. 直接连接到特定的 shard 实例

    php 复制代码
    # 连接到特定的 shard primary 或者 secondary
    mongo --host shard1-host:port
    
    // 有限支持,不是所有版本都适用
    // 通过 mongos 的 adminCommand 进行特定路由
    db.adminCommand({
      $eval: "db.getSiblingDB('local').oplog.rs.find({...}).pretty()",
      nolock: true
    })
  2. 连接到 oplog 集合

    javascript 复制代码
    use local
    db.oplog.rs.find()  // 或在某些配置中为 db.oplog.$main.find()
  3. 查找事务相关操作

    javascript 复制代码
    // 搜索事务操作
    db.oplog.rs.find({
      "ts": {
        $gte: new Timestamp(<conflict_timestamp_seconds>, 0),
        $lte: new Timestamp(<conflict_timestamp_seconds + 5>, 0)
      },
      "op": { $in: ["c", "u", "d", "i"] }  // 命令、更新、删除、插入操作
    }).sort({ts: 1})
  4. 查找特定事务的操作

    javascript 复制代码
    // 如果知道事务ID (lsid),可以查找特定事务的所有操作
    db.oplog.rs.find({
      "lsid.id": UUID("<transaction_id>")
    }).sort({ts: 1})

oplog 的局限性

使用 oplog 定位事务冲突有几个重要限制:

  1. 冲突细节不明确

    • oplog 不会明确标记哪些操作导致了冲突
    • 不会记录被拒绝的操作(只记录成功的写操作)
  2. 事务操作特殊性

    • 多文档事务中的操作通常在事务提交时才会真正写入 oplog
    • 中止的事务操作不会出现在 oplog 中
  3. 时间窗口识别挑战

    • 需要根据冲突发生的时间戳附近查找可能相关的操作
    • 在高负载系统中可能有大量并发事务

实用查询示例

javascript 复制代码
// 根据错误信息中的时间戳查询相关时间段的事务操作
let errorTimestamp = Timestamp(1742240702, 12); // 从错误信息中获取
let startTs = new Timestamp(errorTimestamp.getTime() - 2, 0); // 向前查2秒
let endTs = new Timestamp(errorTimestamp.getTime() + 2, 0); // 向后查2秒

db.oplog.rs.find({
  ts: { $gte: startTs, $lte: endTs },
  $or: [
    { "o.applyOps": { $exists: true } }, // 事务操作
    { "txnNumber": { $exists: true } }   // 事务相关操作
  ]
}).sort({ts: 1})

预防措施

  1. 优化事务范围:尽量减小事务涉及的文档范围
  2. 减少事务持续时间:保持事务尽可能短
  3. 适当错开高竞争操作:考虑应用层面的策略分散写入操作
  4. 适的重试策略,并优化应用程序结构以减少冲突。
相关推荐
AI全栈实验室5 天前
MongoDB迁移金仓踩了5个坑,最后一个差点回滚
mongodb
数据知道6 天前
MongoDB 元素查询运算符:使用 `$exists` 检查字段是否存在及处理缺失字段
数据库·mongodb
数据知道6 天前
MongoDB 批量写操作:`bulkWrite()` 在数据迁移与清洗中的高性能应用
数据库·mongodb
数据知道6 天前
MongoDB 数组更新操作符:`$push`、`$pull`、`$addToSet` 管理列表数据
数据库·mongodb
数据知道6 天前
MongoDB 更新操作符 `$set` 与 `$unset`:精准修改字段与删除字段
数据库·mongodb
数据知道6 天前
MongoDB 数值更新原子操作:`$inc` 实现点赞、计数器等高并发原子操作
数据库·算法·mongodb
数据知道7 天前
MongoDB 数组查询专项:`$all`、`$elemMatch` 与精确匹配数组的使用场景
数据库·mongodb
数据知道7 天前
MongoDB 正则表达式查询:在 MongoDB 中实现模糊搜索与索引优化陷阱
数据库·mongodb·正则表达式
正在走向自律7 天前
文档数据库替换新范式:金仓数据库MongoDB兼容性深度解析与实践指南
数据库·mongodb·国产数据库·金仓数据库
I'mAlex8 天前
金仓数据库平替MongoDB实操解析:多模融合赋能企业文档数据管理国产化升级
数据库·mongodb·kingbasees·金仓数据库