Redis和MongoDB经常被放在一起讨论,因为它们都支持保存带有字段和值的数据集合,甚至都可以存储JSON结构。但二者在文档存储定位上有明显的差异:一个是内存数据结构服务器,另一个是面向文档的分布式数据库。本文不讨论谁更好,而是从存储引擎、文档模型、查询能力、持久化与事务等角度,分析它们在文档存储上的关键区别,帮助你在实际项目里做出合理选择。

存储引擎与内存模型差异
Redis的核心设计是内存优先,所有数据默认保存在内存中,磁盘仅用于持久化恢复。Redis提供了RDB快照和AOF追加日志两种持久化方式,也可以关闭持久化,把Redis当作纯缓存使用。这种设计使Redis的读写延迟非常低,通常可以达到微秒级到亚毫秒级,单节点轻松支撑每秒十万以上的简单操作。对于文档存储来说,Redis可以把一个JSON字符串整体写进String类型,也可以使用Hash结构保存扁平字段,如果安装了RedisJSON模块,还能以树形结构操作JSON字段。但无论如何,数据容量都受到内存大小限制,一旦物理内存不足,扩展成本会迅速上升。
MongoDB则采用不同的思路。从4.0版本开始,默认存储引擎WiredTiger将文档以BSON格式持久化到磁盘,同时利用内存缓存热数据来提升读取性能。写入操作会先写journal日志,再由后台线程刷盘,这样可以在服务异常重启后恢复已经确认写入的数据。MongoDB的容量不受单机内存限制,适合保存GB级别甚至TB级别的业务数据。与Redis相比,MongoDB的读写延迟通常在毫秒级,随机内存访问性能不如Redis,但它能够管理更大规模的数据集,并且对复杂查询有更好的支撑。用一个直观的对比来说,Redis适合做小体积、高频访问的数据层,而MongoDB适合做正式的文档数据库,承担结构化业务数据的存储职责。
存储引擎的差异也影响文档更新的方式。Redis若使用String保存JSON,更新一个字段通常需要读取整个JSON、反序列化、修改后再写回,面对大文档时性能会明显下降。RedisJSON模块虽然能直接操作指定路径的字段,但该模块需要额外安装,而且复杂嵌套结构的更新仍然不如原生文档数据库灵活。MongoDB则可以直接对文档中的任意字段进行部分更新,例如使用$set、$push、$inc等操作符,只修改对应字段,不需要读取或重写整个文档,这在大文档场景下效率更高。
文档结构与查询能力对比
Redis本身并没有文档类型,它用多种数据结构来模拟文档存储。最常见的是把JSON字符串放入String键,操作简单但更新麻烦;也可以用Hash保存字段和值,适合扁平的键值对结构,但Hash不支持嵌套对象和数组。如果要处理真正的嵌套文档,推荐使用RedisJSON模块。RedisJSON允许使用JSONPath语法读取或修改文档的一部分,例如下面的操作可以把用户信息写入键user:1001,并读取姓名和城市字段。
JSON.SET user:1001 $ '{"name":"Alice","age":30,"roles":["admin","editor"],"address":{"city":"Beijing","street":"Changan"}}'
JSON.GET user:1001 $.name
JSON.GET user:1001 $.address.city
MongoDB则原生支持BSON文档模型,文档可以包含嵌套对象、数组、内嵌数组等复杂结构。插入文档时可以直接使用JavaScript对象语法,MongoDB驱动会将它转换为BSON格式。对于上述相同的用户文档,MongoDB的插入和查询示例如下。
db.users.insertOne({
name: "Alice",
age: 30,
roles: ["admin", "editor"],
address: { city: "Beijing", street: "Changan" },
created_at: new Date()
})
db.users.find({
age: { $gte: 18, $lte: 40 },
roles: "admin"
}).sort({ created_at: -1 }).limit(10)
查询能力是两者在文档存储上最显著的差异之一。MongoDB提供完整的查询语言,支持比较操作、逻辑运算、数组匹配、正则表达式、文本搜索、地理空间查询等,还可以对任意字段建立二级索引,包括内嵌文档字段和多键索引。通过聚合管道,MongoDB能够完成分组统计、关联查询、数据转换等复杂分析任务。Redis即使安装了RedisJSON,查询能力也非常有限,它更适合通过精确键值访问数据,或者在Redis外部维护额外的索引结构。如果业务需要根据多个条件组合过滤文档,或者进行聚合分析,MongoDB通常是更合适的选择。
持久化、事务与扩展性差异
Redis的持久化机制以性能为优先,默认情况下RDB快照每隔一段时间生成一次,AOF日志可以配置为每次写入、每秒同步或由操作系统决定。即使配置成每次写入同步,Redis在极端情况下仍可能丢失最后一次操作的部分数据。Redis的事务由MULTI/EXEC实现,它保证一组命令按顺序原子执行,但不支持回滚,中途某条命令出错不会影响其他命令。Lua脚本也可以实现原子操作,适合简单的扣减库存等场景,但跨slot的Redis Cluster事务比较受限。开源版Redis没有提供跨多个文档的ACID事务,因此不适合作为需要强一致性的主数据库。
MULTI SET order:1001 status "paid" INCR inventory:sku:001 EXEC
MongoDB的持久化可以通过write concern来调节写入确认级别,例如设置为majority时,写入只有复制到大多数节点才会向客户端确认,从而降低数据丢失风险。复制集提供自动故障转移,主节点不可用时备节点接替。在事务方面,MongoDB从4.0版本开始支持副本集多文档事务,从4.2版本开始支持分片集群事务。事务具备ACID特性,可以跨多个文档、多个集合执行一致的提交或回滚。例如一个订单系统的下单操作,可以同时更新订单状态、扣减库存、写入账务流水,如果其中一步失败,整个事务可以回滚。
水平扩展上,Redis Cluster通过哈希槽将键分散到不同节点,但跨slot的命令和事务支持有限,需要使用Hash Tag把相关键映射到同一slot。MongoDB分片基于片键范围或哈希策略分配数据,分片集群支持跨分片事务,但事务性能会低于单副本集。下面用表格简单对比两者在文档存储中的关键点。
| 对比维度 | Redis | MongoDB |
|---|---|---|
| 主要存储介质 | 内存 | 磁盘,内存做缓存 |
| 文档模型 | String/JSON、Hash、RedisJSON | 原生BSON文档 |
| 查询能力 | 键值精确查询,JSONPath有限查询 | 丰富查询、二级索引、聚合管道 |
| 事务 | 原子命令块,无回滚 | 多文档ACID事务 |
| 扩展方式 | Redis Cluster哈希槽 | 分片集群,基于片键 |
| 典型延迟 | 微秒到亚毫秒级 | 毫秒级 |
典型场景与选型建议
选择Redis还是MongoDB,关键要看业务对延迟、数据规模、查询复杂度和一致性的要求。如果文档体积较小,通常在几KB以内,访问模式以点查为主,并且可以接受数据在极端情况下丢失,那么Redis非常合适。典型场景包括缓存用户会话、保存购物车临时数据、实现排行榜、实时计数器、消息队列等。比如把用户资料缓存到Redis Hash,可以提升用户中心页面的读取速度,同时设置过期时间自动清理不活跃数据。用Redis做文档存储时,建议将重要数据的原始版本保存在MySQL、PostgreSQL或MongoDB中,Redis只作为加速层。
如果业务文档结构复杂、字段频繁变化、需要按不同条件检索和排序,或者数据量超过单机内存容量,就应该优先考虑MongoDB。典型场景包括内容管理、用户画像、商品目录、博客文章、物联网事件数据等。MongoDB的灵活schema允许不同文档拥有不同字段,因此适合快速迭代的业务。例如电商商品文档中,不同品类可能有不同的属性,使用MongoDB可以免去频繁修改表结构的麻烦。对于需要事务一致性的订单、交易、账户系统,MongoDB的多文档ACID事务也能提供关系型数据库之外的一种选择。
实际项目中也可以把两者组合使用,而不是二选一。例如用户浏览热点数据先查询Redis,缓存未命中再查询MongoDB,同时把MongoDB中的变更回填到Redis。这样既利用了Redis的低延迟,又保留了MongoDB的持久化和复杂查询能力。需要注意的误区是:不要把Redis当作唯一数据源保存重要业务文档,因为内存容量和持久化策略都不适合长期可靠存储;也不要把MongoDB当作纯缓存频繁执行简单键值读写,因为磁盘IO和连接开销会降低整体性能。理解两者在文档存储上的根本差异,才能在不同场景下做出正确的技术选型。