英文原版

好久没写东西,分享一下最近思考的比较多的东西,就当是工作记录了。

从一个很重要的问题出发:如果在今天,我们从新设计一个 Database,架构会是什么样子?

在进入到具体的技术设计之前,我先分享一下我理解的当今(以及未来)的开发者对于数据库的期待:

  • Cost-effective,极致的 Pay-as-you-go,无论是私有云/混合云,还是公有云,云原生的理念在渗透几乎所有的基础软件,能够弹性的利用基础设施是节省成本的关键,谁不喜欢更便宜的东西,我用了多少就给多少钱。另外有着稳定流量的对于性能极端敏感的业务我们另一个话题讨论(因为这种情况下 Serverless 反而可能会更贵)。
  • Ease-of-Use, 易用,使用起来越简单越好,最好把所有基础设施的细节都隐藏掉,极低的心智负担带来极低的上手体验和价值确认。另外我认为目前 SQL 仍然是构建应用最简单的数据库接口,另外对于开发者来说,Modern CLI 体验也是很重要。
  • Unified,这点稍微需要解释一下,如果说第二点是从终端的开发者体验来说,那么这条其实想表达的是:架构的简化,我们这些年发明了太多的数据处理技术,即使做一个普通的应用,可能都要使用 n 种数据库和数据仓库,更头疼的是,规模小的情况和规模大的情况下,很难通过一套方案解决(尽管你的业务形态并没有显著的变化),所以很可能在业务快速增长的时候,开发者需要花费大量的时间进行的基础设施的重构(例如分库分表,或者选用扩展性更好 NoSQL 什么的),这是一个很大的痛点,我们要解决它。

针对上面这些趋势和痛点,映射到技术上:

  1. 内核上使用云原生是架构改造,不仅是存算分离,而是能分离的都分离,因为现在我们要做的已经不再是一个软件,而是一个服务,用户也不会关心服务背后的东西,下面会讲。
  2. 将 Serverless 作为最终的产品形态(注意:Serverless 并不是技术,而是产品形态),反向倒推架构演进路径。
  3. 对于新一代的数据库来说,HTAP 是必选的技术路线,一个预言:未来的数据库都会是 HTAP 数据库。只有纯 AP 对于业务来说是不完整的,TP 非常重要。

带着这些假设,过去这一年,大概做的工作总结一句话就是把: TiDB 大卸八块,然后利用云的基础设施,将它从新拼装起来变成一个数据库服务。最近低调的发布了 TiDB Serverless Tier 就是这个新引擎的第一次亮相。下面聊聊一些技术和工程上的思考。

做服务而不是做软件

今天做数据库,如果你不提供云服务,出门都不太好意思和人打招呼(很快就会是 Serverless)。有很多人(尤其是数据库内核开发者)会低估做一个云服务的复杂性,经典的论调:‘不就是在云上的自动化部署吗?’ 或者 ‘支持一下 Kubernetes Operator?’…其实并不是,甚至目标都应该反过来:**我们要做的并不是一个数据库软件,而是一个数据库服务,当我们用更长的眼光去看的时候就会发现,后者是包含前者的。**这个认知的转变是做好数据库云服务第一步,也是最重要的一步。

我们过去开发程序,不同的模块看到的环境是同构且确定的,例如:开发一个单机上运行的软件,不同的模块虽然可以有逻辑上的边界,但是链接到一起之后,运行起来看到的还是这台计算机的一亩三分地,Everything is a trade-off。即使近几年的分布式系统的兴起,但对于经典的分布式软件来说,大致还是单机软件设计思路的延伸,只是通过 RPC 将多台计算机连接在一起,但是仍然环境是相对确定的,尽管很多软件对于底层的环境变化做了一些适配:例如分布式数据库的动态扩容,数据重均衡 Re-balance 等,但是本质并未变化,只是能够操控和调度的资源变多了。但是在云上,这些假设都发生了变化:

  1. 多样且几乎无限的资源通过 Service API 的形式提供,对于资源的调度和分配可以通过代码完成,这是革命性的变革。
  2. 一切资源明码标价,所以程序优化的方向从过去的一维的榨取最好的性能(因为硬件的成本已经事先支付),变成一个动态的问题:尽量花小钱办大事。

假设的变化带来的技术上的变化:云上的数据库,首先应该是多个自治的微服务组成的网络。这里的微服务并非一定是在不同的机器上,在物理上可能在一台机器上,但是需要能在远程访问,另外这些服务应该是无状态的(无副作用),方便快速的弹性扩展,这个带来对于开发者的转变就是:放弃对于同步语义的坚持,这个世界是异步化且不可靠的。我很高兴我的偶像 Amazon 的 CTO Werner Vogels 在今年 ReInvent Keynote 上也强调了这一点。放弃掉对于同步和单机的幻想,得到了什么?我们看一些例子:

第一,最近几年被聊烂的存算分离🙂。在云上,计算的单位价格比存储要高得多,如果计算和存储绑定,那么就没有办法利用存储的价格优势,另外对于一些特定的请求,对于计算的需求很可能与存储节点的物理资源是完全不对等的(想象一下重型的 OLAP 请求的 Resuffle 和分布式聚合)。另外,对于分布式数据库来说,扩容速度是一个重要的用户体验指标,当存算分离后,原则上扩容速度是能做到极快的,因为扩容变成了:1. 启动新的计算节点 2. 缓存预热;反之亦然。

第二,对于数据库来说,一些内部组件的微服务化,例如:DDL-as-a-Service。传统数据库的 DDL 对于在线业务是有影响的(即使用了 Online DDL),例如添加索引时候,不可避免的需要进行数据回填,这对于正在服务 OLTP 负载存储节点来说会引起抖动。如果我们仔细思考一下 DDL 就会发现它是一个:全局的,偶发的,重计算,可离线进行,可重入的模块,如果有一个共享的存储层(例如 S3),这类模块非常适合剥离出来变成一个 Serverless 的服务,通过 S3 与 OLTP 的存储引擎共享数据。带来的好处毋庸置疑:

  1. 对在线业务也是几乎没有性能影响

  2. 因为按需运行,带来成本下降。

类似的例子还有很多:日志(CPU 使用少,但是对于存储要求高),LSM-Tree 存储引擎的 Compaction,数据压缩,元信息服务,连接池,CDC等等,都是可以且很适合被剥离的对象。在新的 Cloud-native 版本的 TiDB 中,我们使用了 Spot Instances 进行存储引擎的 Remote Compaction,带来的成本下降是惊人的。

在设计云数据库的时候,另一个重要的要思考的问题是:QoS(Quality of service),具体到细节大概是:

  1. 需要定义 WCU 和 RCU,作为控制的单位,因为如果你没有办法定义这个,你将没办法进行资源的分配和调度,乃至定价。
  2. 多租户是必选项,租户之间一定要可以共享硬件甚至集群资源,大租户也可以独占资源(单租户模式是多租户的一种特化),带来的问题:如何避免 Noisy Neiberhood 问题?如何设计 Throttling 策略?如何避免共享的元信息服务 Overwhelm?如何应对极端的热点?

挑战还有很多,我就不一一列举了。很多经验在 AWS 今年那篇 DynamoDB 的新论文中介绍得很详细,大概参考那篇论文即可。

哪些云服务可以被依赖

另一个很重要话题是:云上哪些服务可以依赖?这是因为对于一个第三方厂商来说,跨云(甚至是跨云下,类似混合云)的产品体验是你天然的优势,如果对于特定的云服务依赖得太深太紧,将会让你丧失这份灵活性。所以选择依赖的时候需要非常小心,下面是一些原则:

  1. 依赖接口和协议 ,而不是具体实现,服务内部你随便自己搞,但是给其他服务暴露的接口要通用且不要做过多假设,简单来说就是被调用者心智负担最小化(UNIX 时代留存下来的古老智慧)。一个很好的例子是: VPC Peering 和 PrivateLink,如果按照这个原则,肯定选择 PrivateLink,因为 VPC Peering 倾向于暴露更多的细节给被使用者。
  2. 有行业标准就 Follow 行业标准(S3,POSIX 文件系统),每个云上都有对象存储,而且每个云的对象存储 API 大概都会兼容 S3 协议,这就是好的。
  3. 唯一的例外是安全。如果没办法做到跨云的抽象,也别自己强行造轮子,云有啥就用啥,例如密钥管理,IAM 什么的,千万不要自己发明。

下面举几个例子说明一下,对于 Cloud-Native TiDB 来说,在选择依赖的时候做出如下选择:

  • 存储:S3,就像上面提到的,每个云都会有 S3 协议的对象存储服务。在数据库中使用类似 LSM-Tree 的分层存储,带来的好处是能够通过一套 API 来利用不同层次的存储介质,例如上层的热数据可以使用本地磁盘,下层的数据在 S3 上,通过异步的 Compaction 来将上层的数据交换到 S3 上。这是 TiDB 存算分离的基础,只有数据在 S3 之后,才能解锁 Remote Compaction 等操作。但是带来的问题是:S3 的高延迟注定了它不能出现在主要的读写链路上(上层缓存失效,会带来极高的长尾延迟),对于这个问题,我是比较乐观的:

    1. 如果我们考虑 100% 本地缓存的场景,就退化成经典的 Shared-Nothing 的设计,用于支撑最极端的 OLTP 场景我认为是没问题的(参考现在的 TiKV),额外付出代价只是 S3 上的存储成本 which is 很低。
    2. 如果分片做得足够细,缓存和热点问题是好解决的。
    3. 分层存储中还可以加入 EBS(分布式块存储)来作为二级缓存,进一步削峰(削弱本地缓存失效带来的延迟突变)

    我在 2020 年的一次分享中提到,对于云原生的数据库而言,如何能利用好 S3 会是关键。这个观点到现在还没有变化。

  • 计算:容器 + Kubernetes,和 S3 一样,每个云都有 K8s 的服务,就像 Linux 一样,K8s 是云的操作系统,虽然存算分离做完后,计算相对好管理一点,但是像一些计算资源池的管理,例如 Serverless 集群需要的快速启动(冬眠唤醒),从 0 开始启动建新 Pod 肯定来不及,需要有一些预留的资源,又例如使用 Spot Instance 来处理 Compaction 任务,万一某个 Spot Instance 被回收,能否再快速找个机器继续工作,又例如负载均衡和 Service Mesh…虽然 S3 帮你把最难搞的状态问题解决了,但是这些纯计算节点的调度问题是很琐碎的,如果你选择自己造轮子,那么大概率最后你会重新发明一个 K8s,所以不如直接拥抱。

在云上,还有一个很大的设计问题:文件系统是一个好抽象吗?这个问题来自于在哪层抽象之下屏蔽云的基础设施。在 S3 普及之前,各个大型的分布式系统存储系统,尤其是 Google 的:BigTable,Spanner 等都选择了一个分布式文件系统作为底座(我认为这里面有很深的 Plan9 的痕迹,毕竟 Google 内部这些 Infra 大神很多都是从贝尔实验室来的😄)。那么问题来了,如果有了 S3,我们还需不需要一层文件系统的抽象?我目前还没有想清楚,我倾向于有,理由仍然是存储的缓存,如果有一层文件系统,在文件系统层能够根据文件的访问热度做进行一层缓存,提升扩容时候的预热速度;另一个好处是基于文件系统,生态工具兼容性会更好,很多 UNIX 的工具能直接复用,运维复杂度降低。

将终端用户体验作为优化方向

我在今年的 DevCon 的 Keynote 中提到了一点:云上的数据库如何与现代的开发者体验融合?这个是一个很有意思的话题,因为数据库那么多年了,几乎还是这个样子,SQL is still the king。但是另一方面现在开发者开发的应用以及使用的工具已经和几十年前大不一样了,作为一个从 UNIX 时代过来的老程序员,看到现在年轻一代的开发者使用的眼花缭乱的先进开发工具和理念,只能感叹一代比一代强,虽然操作数据 SQL 仍然是标准,但是数据库软件能否做更多,去融入这些现代的应用开发体验中?

我的答案是:还是能做一些微小的贡献的。例如:

Serverless ,很多人认为的 Serverless 是一个技术名词,我认为不是,Serverless 更重要的是从用户体验角度定义了什么是更好的云上软件的产品形态。或者这是本来就应该是理所应当的:为什么我作为用户需要关心你有几个节点?为什么我需要关心你内部的参数和配置?为什么我点了启动,你要让我再等半小时?…等等这些在我们这个行业里面过去看起来似乎理所应当的事情,其实仔细想想都觉得挺可笑的,举个例子:假设你去买个车,卖车的先送给你一本发动机维修指南,告诉你读完才能上路,车跑得不快,然后告诉你某个发动机参数需要你调一下,每次启动汽车都要等半小时…是不是很奇怪?对于 Serverless 的产品来说,从用户体验来说,最大的意义在于三件事情:

  1. 屏蔽掉配置,降低了使用者的心智负担
  2. 极其快速的启动时间,这点扩展了使用场景和易用性
  3. Scale-to-Zero,在多数场景中降低了使用的成本(当有明显波峰波谷,且你没法预测的场景),在小规模时甚至可以免费。

有了这三点,才能很好的将数据库嵌入到其他的应用开发框架中,这是构建更大的生态的基础。

除了 Serverless 之外,现代的开发者体验(DX)中还包含很多其他的关键要素,例如:

  • Modern CLI:对于开发者来说 CLI 的效率比图形界面高得多,而且更容易通过 Shell 脚本组合其他工具实现自动化。
  • 云端-本地统一的开发/调试/部署体验:没有人想天天碰服务器,本地能搞定的事情,就不要让人 SSH。尤其对于云服务来说,如何在云下开发和调试,目前是一个有很多痛点的市场。
  • Example Code / Demo / 脚手架:新一代的偏向 PLG 的服务提供商,例如:Vercel,Supabase 这一套玩的很溜,想想这也是合理的,对于普通的 CRUD 应用来说,基本的代码框架都是相似的,提供一些快速上手的例子,能够让开发者更快的体会到你的产品价值,也帮助开发者更快的构建他们的应用。

未来的挑战

我上面提到的很多技术内容,基本上都是无人区,很难提前预见到所有的挑战,这也是没办法的事情。这一段作为结尾,列一部分有意思的挑战,虽然肯定不完备,希望能对你有所启发:

  1. 新的产品形态,当不同租户的存储引擎上的数据都在 S3 之后,理论上可以解锁一个更大的基于数据共享和交换的市场(想象一下 Google Docs), 又或者在 S3 上 + MVCC 理论上可以实现类似 Git 似的对于数据的版本控制,想象一下 git checkout 的顺滑体验,只是不同的是,你切换的是你的数据库镜像(我知道已经有云上的数据库产品开始探索这个产品形态),这会带来很多新的应用场景和独特的价值。
  2. 新的商业模式,云是新的计算机,但这个世界应该不会只有几台计算机,除了标准的 SaaS 模式外,还有没有可能将 DBaaS 作为一个整体进行输出,这可能又是一种全新的商业模式(尤其是在和一些二线或者私有云合作的时候),这时候数据库厂商会变成输出数据库服务产品的厂商(有点绕)
  3. 新的研发组织,对于一个数据库厂商来说,过去对于研发和产品的需求几乎只限于内核开发,但是在做云服务的过程中,你不仅是开发者,还会是运维和运营者,而且开发云服务对于研发人员的技术栈的要求和数据库内核是完全不一样的,这里面必然涉及巨大的组织变革和人事调整,如何过渡好?

问题和挑战嘛,永远都是有的,把这个系统做出来的过程,也是我们理解这个系统的过程,最后送上我很喜欢的一句话,来自著名的物理学家理查德费曼:

What I cannot create, I do not understand。