一次几乎不可能的数据库迁移

最初 , 我们把一个文件当作数据库 , 将数据转化为 JSON 大对象写入进去 , 后来 , 它的速度越来越慢 , 我们决定进行数据库的迁移 , 这个过程中我们遇到了一些问题和障碍 , 但最终我们成功完成了这一次不太可能的数据库迁移 。
Brad 加入一家初创公司大约一年前 , 当我刚加入 Tailscale(https://tailscale.com/)时 , 我问 Crawshaw(
https://github.com/crawshaw)的第一件事是:“嗯……你们使用的是什么数据库呢?MySQL、PostgreSQL、SQLite?“我知道他喜欢 SQLite 。
 
“一个文本文件 , ”他回答道 。
“嗯?”
“是的 , 我们将一个大型 JSON 对象写入一个文本文件 。”
“怎么写?什么时候写?写什么?”
“嗯 , 无论什么时候 , 只要有什么东西一变 , 我们都会在我们的单进程中获取一个锁 , 然后重写这个文件!”他高兴地笑着说 。
 
听起来很疯狂 。其实就是很疯狂 。的确 , 它很容易测试 , 但是 , 不能扩展 。这些 , 我们都知道 。但是 , 当时它还行得通 。
 
后来 , 它不行了 。
 
即使使用高速 NVMe 驱动器 , 并且将数据库分成两部分(重要数据和 tmpfs 上的可能丢失的临时数据) , 有些事情也会变得越来越慢 。我们知道这一天终将到来 。文件达到了 150MB 的峰值 , 我们正在以磁盘 I/O 允许的最快速度写入它 。这已经到了极限 。
 
那么 , 迁移到 MySQL 或 PostgreSQL , 如何?或者是 SQLite?
 
不 , Crawshaw 另有主意 。
聊聊大卫的背景故事Tailscale 的协调服务器(我们的“控制面板”)以控制 CONTROL(
https://getsmart.fandom.com/wiki/CONTROL)闻名遐迩 。它目前是单个 VM 上的单个 Go 进程 。它最早的原型使用的是 SQLite 。我们最初的设计与最终的设计有非常大的差异 , 包括同步到客户端机器上的配置数据库 , 以及所有我们最终不再需要的其他概念 。在这个过程中 , 我们每周都要对 SQL 数据模型进行非常大规模的重组 , 这需要大量的键盘输入工作 。SQL 已经得到了广泛使用 , 它持久、有效 , 但将其引入到任何编程语言中几乎都需要做大量的粘合 。(通常大家都试图用 ORM 来避免这种情况 , 用令人生厌的大量魔法字和效率损失来取代那些同样令人生厌的键盘输入工作 。)
 
一天 , 我厌倦了重构 , 就把它彻底丢在一边 , 建了一个内存数据模型进行实验 。这样 , 迭代速度更快了 。几周后 , 一位客户想要试用一下 。我还没有做好提交数据模型和用 SQL 来完成它的准备 , 所以我选择了一条捷径:将持有所有数据的对象包装在一个 sync.Mutex 中 。所有访问都要经过它 , 在编辑时 , 将整个结构传递给 json.Marshal , 然后写入磁盘 。这就是我们用大约 20 行 Go 实现的数据模型持久层 。
 
我们本来计划要迁移为别的语言的 , 但忙着忙着就给忘记了 。
JSONMutexDB 的后面是什么?下一步显然是迁移到 SQL 。我最喜欢的仍然是 SQLite , 但是我不能说服自己把一个快速增长的服务迁移到它上面 。它当然是可行 , 尤其是我们的控制面板的设计并不需要典型的 web 服务高可用性:短时间停机的无非就是使新节点无法登录而已 , 正在工作的网络可以保持正常工作 。
 
其后是 MySQL(或 PostgreSQL) 。我对 1998 年以后的 MySQL 不是特别熟悉 , 但我确信它是可以的 。不过 , 开源数据库的 HA 情况有些令人惊讶:您可以使用传统的滞后副本 , 也可以提交到具有令人非常惊讶的事务语义的无主副本集群 。我对试图在这些语义之上设计一个稳定的 API 或良好的网络图计算并不感兴趣 。CockroachDB(
https://github.com/cockroachdb/cockroach#what-is-cockroachdb) 曾经看起来很有前途 , 而实际上现在仍然很有前途!但对于一个数据库来说 , 它还是相对比较新的 , 我不太放心把一些特性附着在一个新的 DBMS 上 , 因为如果我们需要将这些特性中迁移出来时 , 可能很难做得到 。


推荐阅读