原文《Building a production database in ten years or less》 发布于 2022 年 1 月 25 日,作者 Yury Selivanov(@1st1)。
本文特设 HN 讨论区。
![]()
终于,你厌倦了 $此处填入你常用的数据库品牌,忍无可忍,只能亲自下场,轮一个新的数据库。你有一堆想法,写了很多笔记,坚定不移地相信这就是全世界期待已久的数据库。现在要做的就只剩下,辞掉工作,几个月肝出 1.0 来,对吧?
也许你可以做到!但是——委婉地说——那与我们的经历有些不同。
在即将进入 EdgeDB 后 1.0 时代之前,我们想要回顾一下曾经走过的路——坎坷却充满意义,而且总是比我们想象的要长得多。一路上我们附带搞出了两个主要的开源项目(uvloop 和 asyncpg),为 Python 语言引入了 async/await 关键字,成长为了一个 10 人的开源公司,以及总结出了一些经验教训(希望能长记性),希望对其他想轮数据库的人有帮助。🚀
经过了 2100 个 PR、4600 次集成测试,我们将在农历虎年正月初十——也就是一个星期之后——发布 EdgeDB 的第一个稳定版,作为首届 EdgeDB Day 活动的一部分,该“微型会议”将以线上直播的方式,在两个小时之内,通过一系列闪电演讲,为您解答关于 EdgeDB 的疑问:它有什么用、graph-relational 数据库是什么、明星功能 EdgeQL 等等。
线上直播在油管,可免费注册;会后译者将尝试制作中文字幕并发于 B 站。
让我们回到最初的开始。
2008 年:MagicStack
这一年,我和我的 EdgeDB 联合创始人 Elvis 一同创建了 MagicStack 公司,一个小而精的软件开发作坊。几年里,我们服务了大量不同的客户,从早期创业公司,到财富 500 强的公司如通用电气和微软。
2009 年至 2014 年:一团“乱麻”
我们很早就意识到了,自己在不同项目上一遍一遍地重复解决着同样的问题。这是一种累赘,并且会从本应有趣且充满创造性的工作中抢走时间。
因为厌倦了现有技术,又被 Google Wave 撩了一把,于是我们选择了孵化一套自己的工具集,主要是用 Python 写的,用在了不同的外包项目中。
- 基于组件的声明式用户界面生成器。(眼熟?)
- 一个用于构建后端服务的 RPC 库,支持装饰器和一些“元编程”的功能。
- 一个用于将媒体元素和样式表加载到 Python 文件中的打包器。
![MagicStack 的第一间办公室。 office-2010.jpg]()
这其中的王牌则是一个叫做“乱麻”(Caos,意大利语“混乱”的意思,译注)的数据层“超级 ORM”,提供了:
- 一种面向对象的 schema 定义语言,语法类似 YAML。
- 支持 schema 混入式组合、索引、约束、动态计算属性以及丰富的自省功能。
- 一种支持查询组合与深层嵌套的查询语言(自然地就被叫做 CaosQL 了,乱麻语)。
“乱麻”能解析“乱麻语”查询、按照当前 schema 验证查询合法性并将它们编译成等价的 SQL 语句。在当时,这就是我们的秘密武器,我们在快速交付的同时,还做到了快乐交付。每做一个项目,“乱麻”都能得到一些改进。许多现在 EdgeDB 的重要概念,在当时的“乱麻”里就已经有了,比如链接、schema 多继承、简便的嵌套查询以及对自省功能的重点照顾。
![早期“乱麻”的语法,注意看这段“乱麻语”中给 owner 链接设置了一个 default 值。 caos.jpg]()
因为我们的代码严重依赖“元编程”,所以在 2012 年下半年,当 Python 社区开始寻找志愿者,为 Python 设计一个更好的自省 API(PEP 362)时,我们把握住了这个机会,第一次大举进军开源届,因为我们的 RPC 库同样也需要这个自省 API。
![我和 Elvis 在 2013 年的 PyCon 中与大神讨论。 pycon-2013.jpg]()
之后没多久,我就开始向 asyncio 提交 PR,并在后来的 2013 年成为一名 Python 核心开发者。在 2013 至 2015 年间,我和 Elvis 开启了输出模式,向 Python 语言本身及其生态环境贡献了更多的代码和项目。
2014 年:游艇汇
时值 2014 年,我们选择启动一个自己的产品,因为不想继续再做咨询了。“乱麻”已经给我们打好了十分出众的技术基础,所以我们的第一个产品自然应该是……游艇租赁。你没听错,产品就叫“游艇汇”。
那几个月的努力虽然命途多舛,但还是很有趣的。用“乱麻”和积累的其他工具集,我和 Elvis 得以在破纪录的时间里做出了核心产品,但我们却没有同样迅速地意识到,游艇租赁并不是一门靠谱的生意,真是活到老学到老。
![“游艇汇”的一些状态图和数据结构。 boats-boats-boats.jpg]()
事后,我们重新检视了过去 7 年攒下来的这些轮子,大部分已经被以 JavaScript 生态为主的其他人超越了——React 替代了我们的组件式 UI 库,GraphQL 能处理我们 RPC 库的大部分场景,我们的打包器现在也可以用更好的 Webpack 来替代了。
我们唯独没有在数据库层面看到类似的行业突破,而且老实说看上去还在走下坡路——我们眼睁睁地看着剥离了 schema 的 NoSQL 数据存储软件起高楼,就感觉整个软件行业跟 SQL 和关系模型纠结了许多年后,忽然就决定集体投降了。
直到那一天晚上,我和 Elvis 轻快地走在多伦多下班的小路上,忽然就顿悟了。“乱麻”就代表了技术前进的新方向——不是作为一个 ORM 框架,而是一个数据库。
![早期我们经常碰到人们如此评论 EdgeDB:“所以这就是个 ORM?”我们索性就把这句话放在了墙上,以激励我们自己。 not-an-orm.jpg]()
2015 年:I/O 的纠结
要在 EdgeDB 上取得进展,我们仍需打造几个重要的基础设施。
我们最初的计划是,将“乱麻”从一个 Python 的框架,重构为一个完备的数据库服务器,可以处理通过网络收到的查询请求。而为了能够支持成千上万的并发连接数,我们无法用多线程的阻塞式 I/O 模型(原因可以另写一篇博客了),只能采用异步模型。
差不多的时间里,我们也在设计 EdgeDB 的 Python 客户端库的预设 API,但很快就在数据库事务 API 上碰了钉子——那时后的协程还在用 yield from,所以语句长的让人头大:
tx = yield from conn.start_transaction()
try:
print(yield from tx.query('...'))
except Exception:
yield from tx.rollback()
raise
else:
yield from tx.commit()
我们意识到,如果 Python 能够原生支持 async with 语法,那么事务的写法就可以十分雅致。于是我就打算搞一个 PEP。
async with conn.transaction() as tx:
print(await tx.query('...'))
很快,PEP 的草稿就扩增了 async for(用以支持异步数据库游标)和基本的 async/await。我花了几个星期肝出了 PEP 492,并在 2015 年四月完成了全部工作,最后赶在 Python 3.5 的功能冻结之前通过了审议,PEP 492 正式成为 Python 的一部分。如此重大的提案能被 Python 社区采纳,是我经历过的最刺激的一件事。
2016 年初:Cython 粉墨登场
当我们开始意识到纯 Python 的速度已经无法满足 EdgeDB 处理 I/O 的需要时,我们抓来了 Cython 做小白鼠,因为我们可以用类似 Python 的语法,来快速搭建媲美原生性能的原型。于是我们又花了几个星期的时间,把 libuv 嫁接到了 Cython 上,而 libuv 原本是一个为 Node.js 打造的非阻塞 I/O 网络库。
![在 2016 年 EuroPython 大会上演示 uvloop。 uvloop.jpg]()
这就是 uvloop,可以直接用来替换 Python 内置的 asyncio 事件循环核心,并提供 2—4 倍的性能提升。当时写 uvloop 的博客文章浏览量还挺大的,uvloop 也几乎一夜爆红,现在被我们用在 EdgeDB 里当垫脚石。
2016 年中:开往 Postgres 的高铁
EdgeDB 使用了 Postgres 作为底层支持,因为 Postgres 作为实打实的世界上最先进的开源 SQL 数据库,可以为更好的新型数据库抽象提供终极基础保障。
提示:许多 EdgeDB 的初学者经常困惑,如果 Postgres 已经是一个数据库了,现在 EdgeDB 又是基于 Postgres 的,那么 EdgeDB 就应该是一个 ORM(关系对象映射器)?
我们并不这么认为,因为 EdgeDB 正式地定义了自己的查询语言 EdgeQL,不仅对标 SQL 的全部功能,而且在简明程度上超越 SQL;EdgeDB 不限制使用者的编程语言,而多数 ORM 库都是为某一种特定语言设计的;EdgeDB 有自己的类型系统、标准库、二进制通讯协议、支持多种编程语言的客户端库、命令行工具、开发工作流和使用惯例,所以怎么讲 EdgeDB 都能算得上是一个数据库。
有了 async/await 关键字和一个高性能异步事件循环库,我们开始评估 Python 中异步 Postgres 驱动的现状。可惜,没有一个能打的,所以我们只好自己轮,并在过程中学习到了很多关于 Postgres 二进制协议的优缺点。
2016 年,在西班牙毕尔巴鄂举办的 EuroPython 大会上,我得以顺利演示我们的成果 asyncpg,因为在演讲前两个小时,我们才完成最后的开发工作,Elvis 一如既往地在最后一刻做出了关键的修补。八月份,我们把 asyncpg 发布在了 HN 上,自此获得了广泛的使用,在 GitHub 攒到了 5200 颗星星。
![]()
2017 年至 2018 年:我们做一个数据库吧
有了 async/await、Cython、uvloop 和 asyncpg 的加持,我们终于可以实现 EdgeDB 了。虽然仍在全职做外包咨询,我们还是会在晚上用白板做设计,EdgeDB 现在的主体结构就是当时做出来的:类型系统、EdgeQL 语法和 SDL(schema 定义语言)。有了这些,我们开始尝试推一波技术预览版的 EdgeDB。
为了强迫我们自己按时交付,我们签下了 2018 年 PyCon 大会的金牌赞助商合同,此时距大会开幕仅剩五个月的时间。我们预定了一个展位,印制了 3500 份宣传册,能想到的都做了。自然,我们又一次压哨完成了预览版的开发,在美国克里夫兰一个不知名的民宿里熬了一宿。
![2018 年 PyCon 大会上我们的宣传册。当时 EdgeDB SDL 的语法还需要靠代码缩进,花括号 是 2019 年才加上的。 booklet.jpg]()
结果宣传效果非常不错,PyCon 上人们很喜欢 EdgeDB,我们回答了与会者成百上千的疑问,收到的反馈堆成山,我们兑现了 EdgeDB 的交付承诺。
![我们在 PyCon 的展位:Victor、Nadia、Elvis 和我。 pycon-2018.jpg]()
2019 年初:第一个 alpha 版本
从这时起,我们降低了外包咨询的工作比重,减少到够糊口即可,从而腾出时间专攻 EdgeDB 的第一个 alpha 版本。前面的技术预览版虽然是个不错的原型,但想要发布第一个版本,EdgeDB 还需一些火候。
我们严肃整顿了 EdgeDB 的类型体系,优化了运算符和类型转换系统的安全性和可用性,增加了内置的 GraphQL 支持,根据我们在 asyncpg 的经验和对 Postgres 协议的了解,设计了一套 EdgeDB 的二进制通讯协议,以及基于此协议的 Python 客户端。这段时间之后,EdgeDB 的主体已然定型。
![现今的 EdgeQL 有着简明的语法,支持深嵌套查询和计算属性。 edgeql.jpg]()
2019 年四月,正好在展示 EdgeDB 技术预览版一年之后,我们发布了第一个 alpha 版本,并在 HN 获得了不错的反响(中文版收录在《如何让数据库工效翻十倍》)。不多久,我们另外一篇博客文章《SQL 就这?我们能做得更好》又在 HN 登顶(在 V2EX 也有不少精彩讨论)。这增强了我们的信念:人们对现有数据库的开发体验有相当程度潜在的不满,我们必须继续努力。
2019 年末:EdgeDB 公司
在 2019 年下半年,我们将公司总部从加拿大多伦多迁到了美国旧金山,并成立了 EdgeDB 公司。
![我们在美国的第一间办公室。 sf-office.jpg]()
2020 年:alpha 之年
这一年里,我们的公司从最初的 3 个人成长到 9 个人,并发布了 6 个后续 alpha 版本。
![最初的 6 个 alpha 版本。 alpha-year.jpg]()
这些版本包括了一大堆新工具,包括:
- 一个用 Rust 写的新命令行工具(CLI);
- 一套数据结构迁移系统(migration),以及配套的 CLI 开发工作流;
- 持续优化的二进制通讯协议;
- 一个 JavaScript 客户端库;
- 备份与恢复功能;
- 一种新的 EdgeQL 语法,用于自动创建或更新数据(upsert);
- 以及最棒的交互式查询工具。
不嫌麻烦的话,你可以查阅我们的博客,那里有全部 6 个 alpha 版本的详细演进过程。
2021 年:beta 之年
EdgeDB 的第一个 beta 版本发布于 2021 年二月份,在后续的一年里,我们总共发布了三个 beta 版本和三个 RC 版本。
![2021 年发布的版本:3 个 beta 和 3 个 RC(第四个也是最后一个 RC 版发布于 2022 年)。 beta-year.jpg]()
随着 EdgeDB 的核心产品越来越成熟,我们将工作的中心转到了开发工作流上——如何让 EdgeDB 的开发者体验全面超越所有的现存数据库?包括数据库安装、数据库实例管理、执行 migration、文档阅读、命令行工具的使用、查询数据等等。
嗯……经过不懈的努力,我们重新设计了我们的命令行工具(RFC 1006),升级了客户端库,设计了更符合人类工效学的 API 接口,并增加了对 Go 和 Deno 的支持,捎带手还修了一个 Deno TLS ALPN 的功能。我们重写了 EdgeDB 的文档,发布了一本叫做《EdgeDB 易经》的交互式图书(中文版在来的路上了!),并且还打造了一份华丽的笔记风格的 EdgeQL 教程(中文版已排期!)。我们还着手开发了一个用于 TypeScript 的查询构造器,或将 ORM 扔进历史的垃圾箱——欲知后事如何,且听下回分解。👀
2022 年:飞向稳定,浩瀚无垠!
这就到了现在了,一个星期之后,我们将正式发布 EdgeDB 1.0 稳定版,希望你能喜欢。至少可以说,这是一段非凡的旅程。
这篇文章到现在都没怎么讲 EdgeDB 到底是什么,有兴趣的话,可以参加我们大年初十举办的发布活动(活动为油管英文,中文字幕版将发在 B 站),这是一个两小时的微型会议,希望能解答你的各种问题。点击领票:
![edgedb_day_register.png]()
Thanks to Colin McDonnell and Elvis for feedback and edits on this post.
欢迎关注我们的官方网站、掘金专栏、知乎专栏和 OSCHINA 项目主页,了解更多资讯。