副本集通过tags实现冷热数据读分离,需在rs.conf()中配置tags并递增version后rs.reconfig();readpreferencetags配合readpreference控制定向读取,但tags不参与写入路由,真隔离需分片+zones。

副本集节点如何配置 tags 实现物理隔离
冷热数据分离本质是让不同生命周期的数据落在不同硬件上,MongoDB 本身不自动拆分数据,但可以通过 tags + readPreference + shard key(若分片)或 zone(4.0+ 推荐)协同控制读写路由。副本集内实现“定向读取”,第一步必须给节点打标签,且标签需在初始化时或通过 rs.reconfig() 显式写入配置。
常见错误是直接在 rs.conf() 输出里手动改 tags 字段后调用 rs.reconfig(),但忽略版本号递增和强制参数,导致重配置失败并报错 replSetReconfig: New configuration must have a higher version than the current configuration。
- 先获取当前配置:
cfg = rs.conf() - 为冷节点(如磁盘慢、内存小的归档机器)添加标签:
cfg.members[2].tags = { "tier": "cold", "region": "archive" } - 更新版本号:
cfg.version++ - 执行重配置:
rs.reconfig(cfg, { force: true })(仅当主节点不可用时才需force,日常应确保主节点在线)
注意:标签值只能是字符串,不能是数字或布尔值;键名无限制,但建议全小写、用短横线分隔(如 "data-lifecycle"),避免空格和特殊字符。
应用端如何用 readPreferenceTags 指定读冷节点
客户端不靠自动识别“冷热”,而是靠显式声明 tag 条件。驱动层(如 Node.js 的 mongodb 包、Python 的 pymongo)都支持 readPreferenceTags 参数,它是一个数组,每个元素是键值对对象,匹配任意一个即满足条件。
典型场景:查询 3 年前订单归档数据,允许延迟,优先走冷节点。
- Node.js 示例:
const client = new MongoClient(uri, { readPreference: 'nearest', readPreferenceTags: [{ 'tier': 'cold' }] }); - 如果冷节点不可用,驱动默认 fallback 到其他匹配
readPreference的节点(如nearest会选延迟最低的),不会报错——这点常被误认为“没生效”,其实是容错机制在起作用。 - 多个 tag 组合(AND 逻辑)需写在同一对象内:
[{ 'tier': 'cold', 'region': 'archive' }];OR 逻辑则分开写两个对象:[{ 'tier': 'cold' }, { 'tier': 'warm' }]
为什么不能只靠 tags 控制写入?
tags 仅影响读操作路由,对写入完全无效。所有写操作(包括 insert、update、delete)始终发往主节点,再由主节点同步到从节点。所以“冷数据写入冷节点”这件事,在纯副本集里无法实现——除非你主动把冷数据写完后,用 mongodump + mongorestore 导出并导入到另一套专用于归档的副本集,或升级到分片集群并配合 zones 和 sh.addShardToZone() 做写入隔离。
容易踩的坑:
- 误以为给从节点打
{ "role": "archive" }标签后,insert({ x: 1 }, { writeConcern: { w: "majority" } })就会避开该节点——实际不会,writeConcern只控制写入确认的节点数,不参与路由 - 在连接字符串里加
?readPreferenceTags=[{%22tier%22:%22cold%22}],但没设readPreference=nearest或类似值,导致标签被忽略(驱动要求两者共存)
真实部署中 tags 和 zones 应该选哪个?
如果你还在用 MongoDB 4.0 以下版本,tags 是唯一选择;但 4.0+ 强烈推荐迁移到 zones(现在叫 chunk distribution zones),因为 zones 支持写入路由、自动 chunk 拆分与迁移,而 tags 纯属客户端侧读策略,服务端无感知。
不过注意:副本集本身不支持 zones,只有分片集群才支持。所以所谓“副本集冷热分离”,本质上只是读负载的软性分流,不是数据物理隔离。真要实现写入级冷热分离,必须分片 + zones + 合理的 shard key(比如用时间字段做前缀),否则副本集再怎么打标签,数据副本始终全量存在每个节点上。
最易被忽略的一点:监控冷节点的 lastApplied 和 lastDurable 时间戳差值。如果冷节点长期 lag,readPreferenceTags 仍可能把读请求路由过去,返回过期数据——得靠应用层容忍或加 maxStalenessSeconds 限制。











