当前位置:   article > 正文

SQLite 的文艺复兴

libsql 用法

公众号关注 「奇妙的 Linux 世界」

设为「星标」,每天带你玩转 Linux !

63a31b65a62152d13c05a6d985c8c46e.png

SQLite的故事

第一次被SQLite震撼到的一个是关于它的测试代码[1],其源码有15多万行,但测试代码和脚本竟有九千多万行。作者Dwayne Richard Hipp是一个完美主义者,为了能自由的开发SQLite,他编写了底层的存储引擎、Parser[2]、源码托管工具Fossil[3],除了C编译器和一些底层库如libc外,Richard几乎从零构建了SQLite所依赖的库或工具。

运行SQLite官网的Web服务器Althttpd[4]也是Richard开发的,所有代码仅在一个C文件中,除了标准C库之外没有依赖任何其他代码库。

当然SQLite官网[5]使用的数据库自然也是SQLite了,网页的动态数据[6]甚至是由200多条SQL语句查询后在0.01秒内渲染完成的。

很难想象这种开发模式能够成功,但Richard做到了,他把SQLite推向了数十亿的设备与终端。由于绝大部分代码都是Richard一个人完成的,SQLite并不是一个开源协作的项目。虽然代码是开放的,但却并没有开源社区的贡献,甚至有人为此专门fork出了SQLite的开源协作版本:libSQL[7]

更多关于SQLite背后的故事可以听下这期Podcast:The Untold Story of SQLite[8]

SQLite的架构

SQLite是一个数据库软件,但与绝大部分数据库系统拥有完全不同的运行方式。大部分数据库(MySQL、SQL Server、PostgreSQL或Oracle)系统是Client/Server的架构,客户端通过特定的协议比如JDBC/ODBC与数据库服务器通信,数据库服务器通过监听某个Socket端口去接收客户端的查询请求,之后将结果返回给客户端。

与其他数据库网络通信的方式相比,SQLite是一个库,它是通过In-Process的方式来与应用程序通信的。当应用程序发出查询请求时,这些请求是通过函数调用的方式在与应用程序相同的线程内执行的。SQLite的数据库也是存放在磁盘上的单个文件。

与其他数据库相比,SQLite的优势就是快,尤其是执行一些小的SQL查询语句,这也是为什么SQLite的官网动态数据能通过200多条SQL语句来查询获取的。SQLite也没有N+1查询性能的问题,原因就是没有其他数据库网络通信的开销:

b97f4cb3c39db4ecc81661c8de7d01da.png

SQLite的架构如下:

2a535fdc53db50eb5d7fc8c5c9157c41.jpeg

SQLite的架构主要包括三大部分:编译器、虚拟机与存储引擎。当应用程序发起查询请求时,SQL语句首先会被编译器解析,然后生成字节码,最后通过虚拟机执行。虚拟机的执行过程中,会调用存储引擎的接口来读取或写入数据。

编译器的主要工作是将SQL语句解析成字节码,然后将字节码编译成可执行的机器码。编译器的主要组成部分是Parser与Code Generator,Parser负责将SQL语句解析成抽象语法树(AST),Code Generator负责将AST转换成字节码。查询计划的生成也是在Code Generator中完成的。

虚拟机[9]是一个寄存器式虚拟机(Register-Based VM),它的主要工作是执行字节码。另外查询优化(Query Optimization)也是在虚拟机中完成的。对此部分感兴趣的读者可阅读这篇文章:How the SQLite Virtual Machine Works[10]

存储引擎的主要工作是读取或写入数据。存储引擎的主要组成部分是B-tree、Pager与OS Interface(也叫VFS):

  • B-tree:SQLite的索引以B-tree的数据结构存储,表数据是以B+tree的数据结构存储。对于此部分感兴趣的读者可阅读这篇文章:SQLite Internals: Pages & B-trees[11]

  • Pager(Page cache):B-tree模块与VFS模块之间的抽象层,提供读、写与缓存磁盘页面的功能。SQLite的原子性、隔离性、持久性都是通过Pager来实现的。

    • Pager提供了两种并发访问模式:回滚日志(Rollback Journal)[12]与预写日志(Write-ahead Log)[13]。与回滚日志相比,预写日志能提供更好的扩展性,能在写入数据时并发读取数据。在预写日志模式下,虽然一个数据库只能拥有一个预写日志文件(-wal文件),一次只能允许有一个写入线程去更新这个文件,但在配置`busy_timeout`[14]后允许有多个写入线程同时运行,不过执行过程仍然是串行化的。

    • 对于此部分我推荐阅读这两篇文章:How SQLite helps you do ACID[15]与How SQLite Scales Read Concurrency[16]

  • OS Interface(VFS[17]):为了提供跨操作系统的可移植性,SQLite使用了一个称为VFS的抽象层。VFS提供了打开、读取、写入和关闭磁盘文件的方法,以及其他特定于操作系统的功能。

以上就是SQLite架构的简单介绍,如果你还希望进一步了解内部实现细节,可以阅读这本开源电子书:SQLite Internals: How The World's Most Used Database Works[18]

理解了这些,你就能够更好的了解下面这些把SQLite玩出花活的文艺复兴类开源项目了。

SQLite的文艺复兴

SQLite是个已经超过20岁高龄的并不新的软件,很多人对它的认知还停留在一个玩具类的数据库,拿它用来做一些简单的本地存储或测试,很少用来在生产系统中使用。但就是这样古老的软件,却长期在Hacker News[19]中被热议,因为一些有趣的项目,让SQLite老树逢春、文艺复兴。

一些你可能觉得SQLite做不到的事:

  • SQLite单机百万TPS并发压测:Scaling SQLite to 4M QPS on a Single Server (EC2 vs Bare Metal)[20]

  • SQLite未来可支持多个并发写入: 官方出品的SQLite HC-tree[21],尝试替换老的B-tree[22]数据结构,能实现多写多读、分布式节点复制与更大的数据库大小限制,压测结果很不错[23]

Serverless / Edge Computing

将静态页面发布到CDN中,然后使用API提供动态更新的能力,这种Jamstack[24]架构的Serverless[25]应用能给业务系统带来极高的伸缩性。这种架构的限制在于,数据需要存放在单独的托管数据库中,并且费用不便宜,而且数据库可能会成为一个性能瓶颈,因为当业务系统部署到多区域时,单节点的数据库的网络开销是个不容忽视的问题。

那如果数据库与业务系统实例在同一个服务器上运行呢?

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/从前慢现在也慢/article/detail/636399
推荐阅读
相关标签