我们为“不造轮子”做足了功课,然后造了个轮子

造轮子最怕的不是重复劳动,是选错地基

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 可配、批量切片等)——这些不是设计出来的,是真跑出来的

小结

“要不要自己造”这个问题的答案,可以收敛成三句话:

  1. 需求到位了——统一存储内核这个需求是真实存在的,而且是多个上层需求叠出来的,不是凭空想的;
  2. 现成的不合适——不是不好,是取舍跟使用方式错位(“地址不可复用”这一条就足够否决);
  3. 重构比自己写贵——把源码 vendor 进来评估过,才知道改造成本高于重写,这不是凭感觉。

接下来几篇会讲具体的设计决策:地址为什么是 128 位(以及一次真实的数据事故)、无锁优先队列为什么没有现成的、段表为什么最后长成了最简单的样子、以及内存可见性这个最容易被忽略的维度。


本文事实来自 TC.Tier 仓库的立项文档、产品定位文档与 FASTER 对比报告。

评论

← 全部文章