我们为“不造轮子”做足了功课,然后造了个轮子
造轮子最怕的不是重复劳动,是选错地基。
TC.Tier 是一个从零自研的存储运行时。常被问:存储引擎这种轮子,市面上不是有很多吗?这篇把立项逻辑写清楚——顺便说一个反直觉的经验:判断”要不要自己造”的方法,不是看有没有现成的,而是看现成的地基能不能长出你要的东西。
需求是被”逼”出来的,不是想出来的
TC.Tier 最早不是一个存储项目,而是一个网关项目的副产品。
配套的网关平台(一个全协议统一网关)在演进中一步步遇到了存储问题:
YARP + ASP.NET + Redis + PostgreSQL
↓ 去掉 Redis(换成自研 Raft 保持一致性的完全控制)
Raft 在机械盘上 fsync 延迟完全不可用 → 上 SSD/NVMe
↓ 现成的 IO 引擎达不到"高速磁盘 + 强一致 WAL"的要求
去掉 YARP/ASP.NET → 全原生 C# 流量平台
↓ 需要一个统一存储内核扛住:Raft WAL / 元数据 / 时序 / 队列 / 缓冲
TC.Tier 从底层重写
每一步都是被上一步逼出来的。网关这种复杂度(配置变更要原子一致、三级存储跨实例一致、分布式连接计数与集群状态)对一致性的要求远超普通反向代理,而这些一致性最终都要落到一个存储层上。
(链里那句”必须 SSD/NVMe”是默认路径下的结论。后来我们配着自研持久化栈把机械盘也做成了可用——代价与边界见第 7 篇。)
为什么不堆中间件
最省事的路是”缓存靠 Redis、业务靠 PG、队列靠 Kafka、WAL 靠独立组件”。我们没走,因为:
- 每一个外部中间件都是独立的故障点 + 运维点 + 二进制依赖;
- 中间件越多,故障面越大、排查越难、一致性越碎。
目标很明确:一个内核原生承载全部负载,网关平台零外部存储中间件依赖。
为什么不用现成的
立项时完整调研过 .NET 生态的存储方案。真正具备生产级潜力的几乎只有 FASTER,评估后有三条根本约束:
| 约束 | 具体问题 |
|---|---|
| 架构 | 地址模型/回收模型/并发模型无法适配:地址 API 是 internal 的、不可复用;单一环形分配器会驱逐;页大小耦合全局 |
| 维护 | 上游已停止活跃迭代,定制语义没有上游支撑,只能大规模改源码 |
| 能力 | 要的不是一个 KV,是能承载 WAL / 元数据 / 时序 / 队列 / 缓冲的统一底座 |
第一条最致命。FASTER 为”强制复用 CPU L3”做了一系列取舍——地址不公开、记录不能跨页、哈希与值耦合——这在它的目标场景里是对的,但与我们的使用方式根本错位:一个 key 必须经过索引哈希才能拿到地址,上层既拿不到地址、也构造不出”按地址读”。地址不可复用,就永远做不了”已知地址直达取值”这条路径。
我们把它 vendor 进来,然后决定自己写
值得一说的是:我们不是”看了看觉得不行”,而是把 FASTER v2.6.5 的源码 vendor 进仓库评估过。评估结论是——基于它重构的成本比自研还高。
原因很具体:地址 API 从 internal 改 public 会牵动全链;哈希与值要拆成两阶段;指针/野指针要全面审计。改完基本已经不是 FASTER 了。
于是路线定为”拿论文不拿代码”:参考算法思想(epoch 回收、并发索引设计等),自己实现一套。
我们想做的那块空白
当时看行业格局,存储系统基本二选一:
| 形态 | 代表 | 缺陷 |
|---|---|---|
| 完整产品,底层封闭 | RocksDB / LevelDB / LMDB | 底层不可组合——想换存储模型只能 fork 改源码 |
| 底层库,无官方产品 | FASTER core | 缺一体化产品——使用者要从零拼 KV/WAL/队列 |
TC.Tier 想做的是两者都给:官方产品(TierKV / TierWAL / TierBlob / TierQueue,开箱即用)+ 可组合的运行时内核(从 IO 引擎到四个中间层,都可以换、可以拼)。产品本身就是内核组合能力的证明——比如”队列 = 日志 + 索引”这种组合,封闭式产品做不到。
投入换来了什么
代价是两个多月的底层开发:IO 引擎 + 四个中间层 + 全套持久化(WAL、快照、恢复、截断、CRC、跨平台 fsync)。
回报是网关平台的一致性层已经跑在它之上,并在集成过程中修掉了几十项生产级问题(降级写、流式快照、跨平台 fsync、CRC 可配、批量切片等)——这些不是设计出来的,是真跑出来的。
小结
“要不要自己造”这个问题的答案,可以收敛成三句话:
- 需求到位了——统一存储内核这个需求是真实存在的,而且是多个上层需求叠出来的,不是凭空想的;
- 现成的不合适——不是不好,是取舍跟使用方式错位(“地址不可复用”这一条就足够否决);
- 重构比自己写贵——把源码 vendor 进来评估过,才知道改造成本高于重写,这不是凭感觉。
接下来几篇会讲具体的设计决策:地址为什么是 128 位(以及一次真实的数据事故)、无锁优先队列为什么没有现成的、段表为什么最后长成了最简单的样子、以及内存可见性这个最容易被忽略的维度。
本文事实来自 TC.Tier 仓库的立项文档、产品定位文档与 FASTER 对比报告。
评论