Secondary节点为何阻塞请求近一个小时?

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS PostgreSQL Serverless,0.5-4RCU 50GB 3个月
推荐场景:
对影评进行热评分析
云原生数据库 PolarDB 分布式版,标准版 2核8GB
简介: 看到Secondary节点上的日志,我的内心的崩溃的,鉴权请求居然耗时2977790ms(约50分钟),经详细统计,这个Secondary节点上,所有16:54之后发起的用户请求,都阻塞到17:54左右才返回,处理时间最长的请求约1个小时。 2016-06-17T17:54:57.575+0800

看到Secondary节点上的日志,我的内心的崩溃的,鉴权请求居然耗时2977790ms(约50分钟),经详细统计,这个Secondary节点上,所有16:54之后发起的用户请求,都阻塞到17:54左右才返回,处理时间最长的请求约1个小时。

2016-06-17T17:54:57.575+0800 I COMMAND  [conn2581] command admin.system.users command: saslStart { saslStart: 1, mechanism: "SCRAM-SHA-1", payload: "xxx" } keyUpdates:0 writeConflicts:0 numYields:0 reslen:171 locks:{ Global: { acquireCount: { r: 2 } }, Database: { acquireCount: { r: 1 } }, Collection: { acquireCount: { r: 1 } } } protocol:op_query 2977790ms
2016-06-17T17:54:57.575+0800 I COMMAND  [conn2740] command admin.system.users command: saslStart { saslStart: 1, mechanism: "SCRAM-SHA-1", payload: "xxx" } keyUpdates:0 writeConflicts:0 numYields:0 reslen:171 locks:{ Global: { acquireCount: { r: 2 } }, Database: { acquireCount: { r: 1 } }, Collection: { acquireCount: { r: 1 } } } protocol:op_query 2416390ms

经过调查,引发备节点阻塞近1个小时主要是对一个很大的集合『后台建立索引 + 删除索引』2个动作导致。

背景知识

  1. Secondary从Primary拉取到一批oplog后,重放oplog的过程会加一把特殊的锁,这个锁会阻塞所有的reader,这么做的原因我个人理解是避免让reader看到中间状态,只有等一批oplog全部应用成功才让客户端可读,避免出现脏读的问题。
  2. 建索引有前台(foreground)和后台(background)2种模式,前台建索引会加在整个过程中对DB加互斥写锁,同一个DB下的读写操作均会阻塞至索引建立结束;后台建索引只会对DB加意向写锁,对DB下的读写无影响。
  3. Secondary重放建立索引的命令时,如果是前台模式,则整个重放建索引的过程都会阻塞所有的reader;如果是后台模式,重放时建索引的动作会放到后台线程里做,只会阻塞reader很短的时间。

为了尽量避免建索引影响业务,通常

  1. 在集合创建的时候,就建立好索引,此时因为集合为空,建索引的开销很小。(以后每次写入,同时会更新索引)
  2. 如果建索引时,集合内已经写入了很多文档,尽量使用后台模式,避免『主节点上影响某个DB的所有读写』以及『备节点上影响所有的读』。

问题分析

在我们遇到的问题里,创建索引使用的后台模式,最终仍然导致Secondary阻塞读reader近1个小时,接下来分析下问题产生的原因,主要事件的过程如下所示。

| time | Primary | Secondary |
| --- | --- | --- |
| 15:07:00| db.coll 开始后台创建索引 |
| 16:32:35 | db.coll 创建索引结束| 从oplog拉取到createIndex的操作,并开始重放|
| 16:54:53 | 删除db数据库下某个索引 | 从oplog拉取到dropIndex的请求并开始重放,此时所有的请求开始阻塞 |
| 17:54:53 | | 重放createIndex结束,开始应答阻塞的请求|

  1. 15:07:00 Primary上发起后台建索引,创建索引的过程耗费近1.5小时
  2. 16:32:25 创建索引完成,Primary上记录oplog,Secondary拉取到oplog并重放该动作,由于是后台建索引,Secondary会启动一个单独的线程来建索引,建索引的过程会对DB加意向锁,所以重放动作只会阻塞reader很短的时间(毫秒级别)。
  3. 16:54:53 用户在同一个DB下发起了一个删除索引的动作,删除动作在主上很快结束,备拉取到oplog开始重放,删除索引的动作需要对DB加互斥锁,而此时Secondary后台建索引还在进行中,已经对DB加了意向锁,导致这个互斥锁需要等待后台建索引结束,而重放oplog时,Secondary占用的特殊锁会阻塞所有的reader,所以从这个时间点开始,所有的reader都阻塞等待了。
  4. 17:54:53 Secondary后台建索引结束,删除索引获得互斥锁并完成删除动作,重放结束,阻塞的reader加锁成功并得到处理。

总结

上述问题主要因为MongoDB的设计机制导致,Secondary节点重放oplog时的锁粒度太大,会阻塞所有的读请求;但如果降低锁粒度,就可能会出现脏读的问题,这对于某些业务场景是不可接受的。目前来说,只要不把『耗时长并且需要同步到备节点』的操作放到业务逻辑里,影响是完全可以忽略的,如果实在无法避免,最终出现了Secondary长时间阻塞的情况,就直接使用默认的readPreference,只从Primary上读数据。

参考资料

相关文章
|
NoSQL MongoDB SQL
MongoShake最佳实践
mongoshake最佳实践,到底该怎么玩?
21568 0
|
存储 监控 NoSQL
MongoDB索引解析:工作原理、类型选择及优化策略
MongoDB索引解析:工作原理、类型选择及优化策略
|
存储 NoSQL 物联网
这些案例展示了MongoDB在不同行业中的广泛应用
这些案例展示了MongoDB在不同行业中的广泛应用
605 4
|
监控 Java Linux
redisson内存泄漏问题排查
【9月更文挑战第22天】在排查 Redisson 内存泄漏问题时,首先需确认内存泄漏的存在,使用专业工具(如 JProfiler)分析内存使用情况,检查对象实例数量及引用关系。其次,检查 Redisson 使用方式,确保正确释放资源、避免长时间持有引用、检查订阅和监听器。此外,还需检查应用程序其他部分是否存在内存泄漏源或循环引用等问题,并考虑更新 Redisson 到最新版本以修复潜在问题。
442 5
|
监控 NoSQL MongoDB
MongoDB中的TTL索引:自动过期数据的深入解析与使用方式
MongoDB中的TTL索引:自动过期数据的深入解析与使用方式
|
存储 监控 NoSQL
【MongoDB 专栏】MongoDB 分片策略与最佳实践
【5月更文挑战第10天】MongoDB 分片是应对大数据量的扩展策略,涉及哈希和范围分片两种策略。分片架构包含分片服务器、配置服务器和路由服务器。最佳实践包括选择合适分片键、监控调整、避免热点数据等。注意数据分布不均和跨分片查询的挑战。通过实例展示了如何在电商场景中应用分片。文章旨在帮助理解并优化 MongoDB 分片使用。
514 3
【MongoDB 专栏】MongoDB 分片策略与最佳实践
|
弹性计算 关系型数据库 数据库
PostgreSQL 数据库实例只读锁定(readonly) - 硬锁定,软锁定,解锁
标签 PostgreSQL , 只读 , 锁定 , readonly , recovery.conf , 恢复模式 , pg_is_in_revoery , default_transaction_read_only 背景 在一些场景中,可能要将数据库设置为只读模式。 例如, 1、云数据库,当使用的容量超过了购买的限制时。切换到只读(锁定)模式,确保用户不会用超。 2、业务上需要对
7457 0
|
存储 JSON NoSQL
深入解析MongoDB的存储原理
深入解析MongoDB的存储原理
深入解析MongoDB的存储原理
|
消息中间件 监控 安全
解密RabbitMQ:你所不知道的端口及其重要性
解密RabbitMQ:你所不知道的端口及其重要性
2236 0
|
机器学习/深度学习 人工智能 算法
milvus源码编译
milvus源码编译
333 1