存储过程回光返照?SQLite逻辑和数据合并


Cloudflare迷人的新SQLite支持的“持久对象”系统,该系统鼓励一种架构风格,您的应用程序创建数千个分散在Cloudflare网络中的小型读写SQLite数据库。

Kenton Varda 介绍了 Cloudflare Durable Object平台的下一代产品,该平台最近从键/值存储升级为基于 SQLite 的完整关系系统。

这是分布式系统设计的一个令人着迷的部分,它提倡一种非常有趣的方式来构建大规模应用程序。

Durable Objects 背后的关键理念是将应用程序逻辑与其操作的数据放在一起。(分分合合)

  • Durable Object 包含与其使用的 SQLite 数据库在同一物理主机上执行的代码,从而实现极快的读写性能。

如何才能大规模地实现这一目标?
单个对象在吞吐量方面天生就受到限制,因为它在单台机器的单个线程上运行。要处理更多流量,您可以创建更多对象。
当不同的对象可以处理不同的逻辑状态单元(如不同的文档、不同的用户或数据库的不同“分片”)时,这是最简单的,其中每个状态单元的流量足够低,可以由单个对象处理。

Kenton 给出了一个航班预订系统的例子,其中每个航班都可以映射到一个具有自己的 SQLite 数据库的专用持久对象——每个航空公司每天有数千个新数据库。

每个 DO(Durable Object) 都有一个唯一的名称,然后 Cloudflare 的网络会处理对该对象的路由请求,无论该对象位于其全球网络上的哪个位置。

技术细节非常吸引人。受Litestream的启发,每个 DO 不断将一系列 WAL 条目传输到对象存储 - 每 16MB 或每 10 秒分批。这还可以通过重放这些已记录的事务来实现长达 30 天的时间点恢复。

为了确保十秒窗口之外的持久性,写入操作在提交后也会立即转发到附近独立数据中心的五个副本,并且只有在其中三个副本确认后才会确认该写入。

JavaScript API 设计也很有趣:它是阻塞的而不是异步的,因为设计的整个重点是提供快速的单线程持久性操作:

let docs = sql.exec(<code>
  SELECT title, authorId FROM documents
  ORDER BY lastModified DESC
  LIMIT 100
</code>).toArray();

for (let doc of docs) {
  doc.authorName = sql.exec(
    "SELECT name FROM users WHERE id = ?",
    doc.authorId).one().name;
}


他们的这个例子中,故意展示了 N+1 查询模式,因为这是 SQLite独有的适合处理的方式。

Durable Objects 的底层系统称为存储中继服务,它已经为 Cloudflare 现有但不同的D1 SQLite 系统提供支持一年多了。