RavenDB — .NET 生态首选的 ACID 文档数据库
是什么
RavenDB 是 2010 年由 Oren Eini(笔名 Ayende Rahien,NHibernate 核心贡献者)在以色列公司 Hibernating Rhinos 启动的开源文档数据库——一句话定位:.NET 友好、跨文档 ACID、自带全文索引的 mongodb 替代品。
日常类比:mongodb 像一个特别擅长收纳的衣柜,每件衣服(文档)单独取放都很快,但你想”把这件外套放进 A 柜的同时把围巾放进 B 柜,要么一起成功要么一起失败”,它说”做不到,我只保证单件的原子性”。RavenDB 多了一把锁——你可以告诉它”这两件得绑成一个事务”,要么都进去要么都不进。
GitHub 约 3.5k star,AGPLv3 + 商业双授权,是 .NET 圈对标 mongodb 的主选项。
为什么重要
- 跨文档 ACID 在文档数据库里是稀有特性:mongodb 直到 4.0(2018)才支持,且性能损失明显;RavenDB 从 v1 起就是默认行为
- .NET 优先:客户端是 C# / LINQ 写的,索引可以直接用 LINQ 表达式声明,写起来像查 List 而不是拼字符串
- 预计算索引:查询永远走索引、不退化成全表扫——这一点和 elasticsearch 同源(都用过 Lucene),但 OLTP 场景更友好
- 集群一致性可调:单文档默认异步多主复制;标了 cluster-wide 的事务走 raft 严格一致,让你按业务挑
核心要点
1. 文档 + metadata 的存储模型
每条文档是 JSON,外加一份 metadata:
{ "@metadata": { "@id": "users/1-A", "@collection": "Users", "@change-vector": "A:23-xyz, B:11-pqr", "@last-modified": "2026-05-31T08:00:00Z" }, "Name": "Jason", "Age": 30}@change-vector 是 RavenDB 特色——它不是单调递增的版本号,而是”每个节点各自的计数器拼起来”,让多主复制能识别出”这条文档在 A、B 两节点都被改过,需要冲突解决”。这是分布式系统里向量时钟的工业实现。
2. 索引是写入时预计算的
很多数据库的索引是”查询时按 BTree 找一遍”,RavenDB 反过来——你声明索引(LINQ 表达式),引擎在写入时增量把文档映射成索引项:
public class Users_ByAge : AbstractIndexCreationTask<User> { public Users_ByAge() { Map = users => from u in users where u.Age > 18 select new { u.Age }; }}代价:索引会异步更新(毫秒级延迟),所以默认查询是”最终一致”。要强一致需显式 WaitForNonStaleResults。
好处:查询永远不会因为漏建索引退化成全表扫——RavenDB 干脆禁止 ad-hoc 全表查,逼你提前声明。
3. RQL 长得像 SQL,跑的是 Lucene/Corax
from Userswhere Age > 25 and search(Bio, "machine learning")order by Age desclimit 10底层 v5 之前走 Lucene 索引(和 elasticsearch 同根),v6(2024)切到自研 Corax——按 RavenDB 工作负载特化(更小的内存占用、更少的 GC),官方测速比 Lucene 快 5-10 倍。
4. 集群:多主异步 + Raft 严格一致
RavenDB 集群由若干节点组成,默认是多主异步复制——任何节点都能写,写完返回,再背景同步到其他节点。冲突用 @change-vector 检测,可以脚本化解决(“取最新”、“合并字段”等)。
如果业务需要严格一致(库存、转账),打开 cluster-wide transaction:写入要走 raft 共识,多数节点确认才算成功。代价是延迟从毫秒升到几十毫秒。
5. Subscriptions:数据库推给你
订阅一个查询,服务器主动把匹配的文档流推给客户端,断线重连后从断点续传:
var sub = store.Subscriptions.Create<Order>( o => o.Status == "Pending");worker.Run(batch => { foreach (var order in batch.Items) ProcessOrder(order.Result);});适合事件源 / CQRS 架构——投影服务订阅”领域事件”集合,数据库当消息队列用。
6. ETL 内置
RavenDB 自带把数据同步到外部的 ETL 任务:到 SQL Server、到 Kafka、到 ElasticSearch、到另一个 RavenDB。配置一段 JS 转换脚本即可,省掉 Debezium / Airbyte 这一层。
实践案例
案例 1:电商订单 + 库存的强一致
下单要同时扣库存和写订单。两个文档分布在不同 collection,传统文档库只能在应用层用补偿事务。RavenDB 直接:
using var session = store.OpenSession(new SessionOptions { TransactionMode = TransactionMode.ClusterWide});var stock = session.Load<Stock>("stocks/sku-123");if (stock.Quantity < 1) throw new OutOfStockException();stock.Quantity--;session.Store(new Order { ... });session.SaveChanges(); // Raft committed,要么都成功要么都失败案例 2:博客全文搜索 + 文档查询合一
用 postgresql 你得装 pg_trgm 或外接 elasticsearch;用 mongodb 文本索引能力有限。RavenDB 一句 search() 走 Corax,OLTP 库自己带全文搜索,省一套基础设施。
踩过的坑
- AGPLv3 是真的”传染”:把 RavenDB 嵌入闭源产品要么开源整个项目,要么买商业授权——不是 Apache 那种宽松协议,正经商用前要先和法务对齐
- 索引异步更新带来”读自己写不到”:刚
SaveChanges完立刻查,可能查不到最新数据。需要WaitForNonStaleResults或重读文档(按 ID 永远是强一致) - 没有 ad-hoc 查询:习惯 mongodb 随便
db.find({})探索的人会不适,RavenDB 强制先声明索引 - 生态小:星数 3.5k vs mongodb 28k,遇到非主流问题 Stack Overflow 上不一定有答案,多数得啃官方文档或 Ayende 博客
适用 vs 不适用场景
适用:
- .NET 项目想要”文档库 + 事务 + 全文搜索”一站式后端
- 事件源 / CQRS 架构,subscriptions 当事件总线
- 中小规模数据(百 GB 到几 TB),需要强一致但又不想用关系数据库
不适用:
- 需要 PB 级水平扩展 → mongodb / Cassandra 生态成熟得多
- 全栈非 .NET 的项目 → 客户端体验在 Java / Go / Node 不如 .NET 顺滑
- 需要复杂分析 / OLAP → elasticsearch 或专用列存更合适
- 极简 KV 缓存 → 用错地方,Redis 之类更轻
历史小故事(可跳过)
- 2010 年:Oren Eini 在 NHibernate 维护多年,受够”对象关系阻抗失配”的痛,决定造一个文档库
- v3(2015):第一个被 .NET 社区广泛接受的稳定版
- v4(2018):重写存储引擎 Voron(基于 LMDB 思路的 B+ Tree),性能跃迁
- v5(2021):引入时间序列和分布式计数器
- v6(2024):自研 Corax 替换 Lucene 索引引擎
- v7(2025):原生向量搜索,拥抱 RAG 浪潮
学到什么
- 跨文档 ACID 在文档库里要付出复杂度代价——RavenDB 选择把这个复杂度内化在引擎里(默认事务),mongodb 选择把它暴露给用户(4.0 后才加,要显式
startTransaction) - 预计算索引 vs 查询时索引 是文档库的两条路:elasticsearch / RavenDB 走预计算,mongodb / postgresql 走查询时——前者写慢查快,后者反过来
- Voron / Corax 都是”不重新发明轮子,但按工作负载重写”——LMDB 已有 B+ Tree、Lucene 已有倒排索引,但通用版总有不合身的地方,自己写一份按业务场景特化是工业数据库常见路线
- 创始人长期写技术博客(Ayende.com)是 RavenDB 区别于其他闭源产品的关键——存储引擎设计细节都公开,让用户敢用
延伸阅读
- 官方文档:RavenDB Documentation(.NET / Java / Node 客户端教程)
- 创始人博客:Ayende @ Rahien(深入存储引擎、Voron 设计、Raft 实现)
- mongodb —— 文档库标准答案,对比看跨文档事务的不同实现
- elasticsearch —— 同样基于 Lucene,对比看 OLTP vs 分析侧重
- raft —— RavenDB 的 cluster-wide 事务底层协议
关联
- mongodb —— 同领域最大对手,对比看事务和索引模型
- couchdb —— 都讲 MVCC + 复制,CouchDB 没跨文档事务
- elasticsearch —— 共享 Lucene 血统,定位互补
- postgresql —— JSONB 路线的关系派对手
- raft —— cluster-wide 事务底层共识
- neo4j —— 邻居领域(图)做对照