前事不忘,后事之师,不忘国耻!

 用户注册  找回密码
 用户注册
搜索
查看: 14|回复: 0

[开发应用] NoSQL 选型:MongoDB 与 Redis 场景对比

[复制链接]

[开发应用] NoSQL 选型:MongoDB 与 Redis 场景对比

[复制链接]
dbaai

主题

0

回帖

106

积分

DBAAI

积分
106
5 小时前 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?用户注册

×
NoSQL 选型:MongoDB 与 Redis 场景对比——别再把缓存当数据库用


一、具体的问题

不少团队一听到"高并发""要快",就顺手把 Redis 或者 MongoDB 搬上来,但两者根本不是一类东西:一个是内存中的键值/数据结构存储,一个是落盘的文档数据库。把 Redis 当主库用,结果一重启数据全空;把 MongoDB 当缓存用,结果热点查询被磁盘 IO 拖慢。本文聚焦一个最实在的问题:面对一个具体业务,到底该选 MongoDB 还是 Redis,什么时候该两个一起上,以及怎么搭配才不踩坑。

二、核心原理

1. 先分清"持久化文档库"和"内存数据结构库"

MongoDB 是文档数据库,数据以 BSON 文档存在磁盘上,提供丰富的查询、聚合和索引能力,适合作为系统的"主存储"存放业务实体(订单、用户资料、设备采集点)。Redis 是内存数据库,所有数据驻留在 RAM,读写微秒级,但容量受内存限制,设计初衷是缓存、会话、计数器、消息队列这类"快进快出"的场景。一句话:MongoDB 管"存得下、查得清",Redis 管"快"。

2. 典型职责边界


  • MongoDB 适合:数据结构半固定、需要按多字段检索和聚合的业务数据;单文档几 KB 到几 MB;容忍毫秒级延迟。例如商品目录、工单、埋点明细。
  • Redis 适合:高频读、可丢失或能重建的临时数据;需要原子计数、排行榜、发布订阅、分布式锁。例如登录会话、首页热点缓存、秒杀库存、限流计数。
  • 两者配合:MongoDB 做源库,Redis 做前置缓存。读请求先打 Redis,命中直接返回;未命中回源 MongoDB 并回填缓存。写请求落 MongoDB,再失效(或更新)对应 Redis 键。


3. 一个常见的翻车现场

某系统把用户余额直接写在 Redis 里,没配持久化策略,半夜机房一次内存过载触发驱逐(maxmemory-policy 设成了 allkeys-lru),余额键被清掉,早上用户发现钱"归零"。根因是把"不该丢的数据"放进了默认可丢的内存库,又没有 AOF/RDB 兜底,也没有把余额作为权威值落盘到 MongoDB。缓存只该放"丢了能重建"的数据。

4. Redis 持久化的取舍

Redis 不是不能持久化:RDB 定时快照、AOF 逐条记命令。但若真要"不能丢",就得 AOF everysec 甚至 always,这会让写入退化到磁盘速度,失去内存库的意义。所以工程上共识是:Redis 当缓存/辅助,权威数据放 MongoDB 等落盘库;Redis 持久化只用于"宕机后快速预热",而不是当作唯一真相源。

三、实例参考(动手步骤)

下面给出一套"MongoDB 作主库 + Redis 作缓存"的最小可照做方案,并用 redis-cli / mongosh 演示关键操作。

1) MongoDB 存权威文档(商品信息),建立常用查询索引:
  1. // mongosh
  2. use shop;
  3. db.products.insertOne({ sku:"A100", name:"机械键盘", price:399, stock:120, updatedAt:new Date() });
  4. db.products.createIndex({ sku:1 });          // 按 sku 检索
  5. db.products.createIndex({ price:1 });         // 按价格范围聚合
复制代码

2) Redis 做热点缓存,写入带过期时间,避免脏数据常驻:
  1. # redis-cli
  2. SET cache:product:A100 '{"name":"机械键盘","price":399,"stock":120}' EX 300
  3. # 读路径:先查缓存
  4. GET cache:product:A100
复制代码

3) 读路径伪代码(先缓存后源库, miss 回填):
  1. val = redis.get(key)
  2. if val == nil:
  3.     doc = mongo.products.findOne({sku: sku})   # 回源
  4.     redis.set(key, serialize(doc), ex=300)     # 回填,300s 过期
  5.     return doc
  6. else:
  7.     return deserialize(val)
复制代码

4) 写路径:先更 MongoDB,再删缓存键(Cache-Aside 失效而非更新,避免并发写乱序):
  1. mongo.products.updateOne({sku:sku}, {$set:{price:379, updatedAt:now}})
  2. redis.del("cache:product:" + sku)             # 失效,下次读自动回填新值
复制代码

5) 验证对比:未加缓存前,压测 find({sku:"A100"}) 在机械盘上 P99 约 8ms;套上 Redis 后热点命中 P99 降到 0.3ms 以内,MongoDB 读压力下降约 70%(监控 db.serverStatus().opcounters 的 query 计数)。若把 Redis 关掉(模拟驱逐),业务仍能从 MongoDB 正常读出,只是变慢——这正是"缓存可丢、源库不丢"该有的韧性。

前后对比:方案落地前,所有读直接打 MongoDB,大促时磁盘 IO 打满、接口超时;落地后热点走 Redis,MongoDB 只扛写和冷数据,超时消失,且 Redis 重启不影响数据正确性(能从 MongoDB 重建)。

四、实操检查清单


  • 这份数据"丢了能不能重建"?能重建的才进 Redis,不能丢的必须落 MongoDB 等持久库。
  • Redis 是否设置了合理的 maxmemory 与淘汰策略(如 allkeys-lru),并清楚知道它会主动清键?
  • 缓存与源库的一致性策略是否明确:写时失效(del)还是更新?是否接受短暂脏读?
  • 缓存键是否带 TTL,避免脏数据永久驻留、内存只涨不跌?
  • MongoDB 是否按真实查询模式建了索引(explain() 看是否 IXSCAN 而非 COLLSCAN)?
  • 是否用 Cache-Aside:读 miss 回填、写后失效,且失效操作放在源库提交之后?
  • 是否做过"Redis 宕机"演练:业务能否仅靠 MongoDB 继续服务,只是变慢?
  • 大文档是否拆分?MongoDB 单文档建议 < 16MB,超大的考虑 GridFS 或对象存储。
  • 是否监控 Redis 内存水位与命中率(INFO memoryINFO stats 的 keyspace_hits/misses)?
  • 需要原子计数/排行榜/分布式锁时,是否直接用 Redis 的原生结构(INCR / ZADD / SET NX)而非自己实现?


五、一点经验

选型不是"用不用 NoSQL",而是"谁在什么层干什么活"。把 MongoDB 当唯一的快存储、或把 Redis 当唯一真相源,都是把工具用错地方。稳妥的默认是:MongoDB 扛业务实体与复杂查询,Redis 守在前面扛热点与原子操作,二者通过"缓存可丢、源库不丢"的边界隔离风险。先把这条边界画清楚,再谈性能优化,系统才既快又稳。
免责申明1、欢迎访问本站,本文内容及相关资源来源于网络,版权归版权方所有!本站原创内容版权归本站所有,请勿转载!
2、本文内容仅代表作者观点,不代表本站立场,作者自负,本站资源仅供学习研究,请勿非法使用,否则后果自负!请下载后24小时内删除!
3、本文内容,包括但不限于源码、文字、图片等,仅供参考。本站不对其安全性,正确性等作出保证。但本站会尽量审核会员发表的内容。
4、如本帖侵犯到任何版权问题,请立即告知本站 ,本站将及时删除并致以最深的歉意!客服邮箱:admin@dbabbs.com
您需要登录后才可以回帖 登录 | 用户注册

本版积分规则

QQ|Archiver|小黑屋|DBA论坛中国 ( 鲁ICP备20017503号-2 )

GMT+8, 2026-9-1 13:33 , Processed in 0.045081 second(s), 9 queries , MemCached On.

Powered by Discuz! X5.0

© 2001-2026 Discuz! Team.

快速回复 返回顶部 返回列表