RethinkDB — 让数据库自己把更新推给客户端的先驱
是什么
RethinkDB 是一个开源文档数据库,长得像 MongoDB,但有一个谁都没做过的核心能力:客户端可以订阅一条查询的结果,结果一变就被推过来。
日常类比:传统数据库像点外卖——你点一次拿一次,下次想知道菜单更新就得再点一次。RethinkDB 像微信公众号——你关注一次,新内容自动推到手机。
写法长这样(JavaScript 驱动):
r.table('messages') .filter({room: 'lobby'}) .changes() .run(conn, (err, cursor) => { cursor.each((err, change) => console.log(change)) }).changes() 是关键。这不是普通查询——它告诉服务端:把这条查询挂着,一有命中行变化就把差量推给我。聊天室、协作编辑、实时仪表盘,都不再需要前端轮询。
为什么重要
不理解 RethinkDB,下面这些事都没法解释:
- 为什么 Firebase / Supabase / Convex 后来都把『实时订阅』当卖点——它们是 RethinkDB 思想的徒弟
- 为什么 Materialize / Noria 这些增量视图维护数据库会出现——把『订阅一条查询』推到 SQL 体系
- 为什么一个技术上做得很扎实的产品会商业失败——选错市场、错过窗口期
- 为什么 2026 年仍有人讨论它——失败案例的教学价值有时大于成功
核心要点
RethinkDB 把『查询』和『订阅』合二为一。这个设计可以拆成 三层:
-
ReQL 把查询当函数链:每个驱动语言(JS / Python / Ruby)里 ReQL 是嵌入式 DSL,链式调用——不像 SQL 拼字符串。
r.table(x).filter(y).changes()是真正的代码,编辑器能补全、能 lint。这个设计的副作用:查询能被组合、被高阶函数包装,比 SQL 灵活很多。 -
服务端把活跃订阅当一等公民:每个 changefeed 是一条长连接 + 服务端维护的过滤器 + 游标。写入命中过滤器时,服务端立刻把差量(旧值 + 新值)推下去。不是轮询、不是触发器、不是 oplog 拉取——是查询引擎本身的扩展。这一点是 RethinkDB 与 MongoDB 最本质的差别:MongoDB 的『实时』是事后给 oplog 套层壳,RethinkDB 是从查询引擎自下而上重写。
-
集群层用 Raft 管元数据:分片按主键范围切,副本之间用 Raft 同步配置变更(谁是 leader、表怎么分)。数据写入走自研 MVCC + B-tree 引擎。这一层和 spanner / raft 是一个家族——元数据要强一致,数据本身可以最终一致。
实践案例
案例 1:聊天室前端不再轮询
r.table('messages').filter({room: 'lobby'}).changes()每条新消息进库,前端立即收到 {old_val: null, new_val: {...}}。删除是 {old_val: {...}, new_val: null}。整个『推送基础设施』就是这一行查询。
案例 2:实时排行榜
r.table('scores').orderBy({index: r.desc('score')}).limit(10).changes()榜单一旦有人挤进前 10,服务端推 {old_val: 旧第 10, new_val: 新进来的}。前端收到差量直接更新 UI——不需要重新拉整张表。
案例 3:跨表 join 的订阅
r.table('orders') .eqJoin('user_id', r.table('users')) .changes()订单或用户任意一边变化,订阅都会被触发。这一点是 changefeed 比 kafka 这类消息队列更高层的地方——队列只能告诉你『某行变了』,changefeed 告诉你『某条业务查询结果变了』。
案例 4:客户端断线重连不丢事件
ReQL changefeed 有 includeInitial 和 squash 参数。客户端断线重连后,可以指定『把我离线期间漏掉的差量补给我』,配合本地缓存就能做出 Notion / Figma 那样的离线优先体验。这是 Firebase Realtime Database 后来卖得最响的功能之一——RethinkDB 早做了几年。
踩过的坑
-
changefeed 不是免费的:服务端要为每个订阅维护活跃过滤器和游标,连接数线性增长成本。万级长连接时内存和 CPU 都吃紧——这是后来 Firebase / Supabase 设计时不得不解决的同一道题。
-
顺序保证有限:单分片内有序,跨分片全局顺序不保证。写复杂聚合订阅会踩这个——比如『按时间排序』如果跨分片,前端可能先看到第 5 条再看到第 3 条。
-
2016 年公司倒闭:代码转给 Linux Foundation 作为开源项目存活,社区版仍在维护,但商业级运维支持几乎为零。生产环境慎选——故障时没人兜底。
-
ReQL 学习曲线非零:和 SQL 思维不兼容,团队迁移成本高。新人来了要重新学一套查询语法,BI 工具基本不支持。
适用 vs 不适用场景
适用:
- 实时协作产品的快速原型——聊天、协作编辑、实时仪表盘
- 中小数据量 + 重订阅的场景——changefeed 是杀手锏
- 教学/研究——想理解『查询即订阅』思想的最直白样本
- 想做出 Notion / Figma 那种实时多人协作体验,但不想自己搓订阅基础设施
不适用:
- 需要强一致 ACID 跨行事务 → 看 spanner / Postgres
- 数据量到 PB 级 → 看 dynamo 系或云原生数据库
- 需要 SQL 生态(BI 工具、ORM 库)→ ReQL 不兼容
- 生产环境要厂商支持 → 没有商业实体了,出问题只能自己读源码
历史小故事(可跳过)
- 2009 年:Slava Akhmechet 三人在 YC 创立,最初想做 SSD 优化的存储引擎
- 2012 年:1.0 发布,叙事从『更快的存储』转向『实时数据库』
- 2015 年:Hacker News 一片好评,技术上口碑顶峰,但收入一直起不来
- 2016 年 10 月:公司倒闭。Slava 写了博客 Why RethinkDB Failed,复盘两个错——选错市场(开发者喜欢免费工具,但买单的是企业)和优先正确性而牺牲默认就快的初印象(每次基准测试都被 MongoDB 甩开)
- 2017 年起:Linux Foundation 接管代码,社区维护至今
之后 differential-datalog / Materialize / Noria 等系统把『订阅一条查询』思想推到 SQL 和增量视图维护方向。Firebase / Supabase Realtime / Convex 在文档数据库这一支继承了它的实时基因。
学到什么
- 查询可以是订阅对象,不只是一次性请求——这是把数据库从拉取式变推送式的关键洞见,后来被无数实时基础设施继承
- 技术做得对 ≠ 商业能成——开源繁荣与商业失败可以同时存在;选错市场是个独立维度
- changefeed 是高层抽象:比 Kafka 这类队列更贴近业务——队列只能告诉你『某行变了』,changefeed 告诉你『某条业务查询结果变了』
- 失败案例的教学价值:Slava 那篇 Why RethinkDB Failed 是产品决策的必读,比成功案例更能学到东西
- 思想可以脱离实现存活:RethinkDB 公司没了,但『查询即订阅』的思想已经被 Firebase / Convex / Materialize 接力跑下去
延伸阅读
- 失败复盘:Slava — Why RethinkDB Failed(产品 PM 必读,半小时读完受益终身)
- 官方 changefeed 文档:RethinkDB Changefeeds(看完就能跑 demo)
- 源码:github.com/rethinkdb/rethinkdb(C++ 写的,有兴趣可以读 changefeed 实现)
- spanner —— 强一致分布式 SQL,另一种架构哲学
- kafka —— 队列式订阅,与 changefeed 对比
- raft —— RethinkDB 元数据层用的共识算法
关联
- spanner —— 同样用共识算法管元数据,但目标是强一致 SQL
- kafka —— 同样做『订阅』,但订阅对象是行变化而不是查询结果
- raft —— RethinkDB 集群元数据同步用的算法
- dynamo —— 同代分布式存储,走最终一致路线
- differential-datalog —— 把『订阅查询结果』思想推到 Datalog/SQL 体系
思想血脉(可选段)
把 RethinkDB 放进时间线看才能看清它的价值:
- 上游影响它的:1990s 主动数据库(active database)研究、Datalog 增量维护、CEP(complex event processing)系统——这些学术想法 RethinkDB 是第一个把它们包成『普通开发者也能用』的产品形态
- 下游被它影响的:Firebase Realtime(KV 层订阅,更窄)、Supabase Realtime(Postgres + 订阅)、Convex(更彻底的查询即订阅)、Materialize(SQL + 增量视图维护)、Noria(学术原型,现已停止维护)、Linear / Replicache 这类同步引擎
它的位置:第一个把订阅做成查询引擎一等公民的工业产品。这条线索一旦看清楚,再去看后辈就会觉得『哦,他们其实在解决同一道题』。
反向链接
- differential-datalog —— DDlog (Differential Datalog) — 输入只改一条,引擎只算受影响的那一小块
- dynamo —— Dynamo — 让购物车永远能写入的分布式存储
- mongo —— MongoDB — 文档数据库服务端开源实现
- raft —— Raft — 易理解的共识算法
- spanner —— Spanner — 全球分布式 SQL 数据库