MariaDB 非阻塞客户端 API 与 node.js

去年夏天,我在 MariaDB 中实现了一个非阻塞客户端 API,并将其包含在 MariaDB 5.5 版本中。但不知怎么的,我一直没来得及宣布它。

然而,这并没有阻止 Brian White 注意到它,并用它实现了一个新的 node.js mysql 绑定,名为 mariasql

现在,node.js 是一个单线程、事件驱动的 Web 应用程序服务器开发框架。在这种框架中,所有 I/O 操作都是非阻塞或异步进行的,所有其他可能需要等待外部事件的操作也是如此。有一个单一的事件循环,它使用 poll() 或类似的系统调用来等待任何待处理的 I/O 或其他事件完成,然后分派适当的事件处理程序。这种框架通常用于代替多线程模型,例如,如果线程不可用,或者为了避免复杂的线程间同步和昂贵的上下文切换开销。

这种框架正是 MariaDB 中的非阻塞客户端库发挥作用的地方。知识库包含所有详细信息,但基本思想如下

要运行一个查询,应用程序会进行如下调用

mysql_real_query_start(&status, mysql, “SELECT * FROM t1”);

这将在套接字上发送数据,服务器将开始执行查询。但与常规 API 不同,它会立即返回,而不是等待结果。然后应用程序可以检查正在运行查询的状态;在这种情况下,它发现查询正在等待数据到达某个套接字描述符。然后应用程序将注册一个回调,并在其主循环的 poll() 调用中包含该套接字描述符。当数据到达时,回调触发并调用

mysql_real_query_cont(&status, mysql, <data_is_ready>)

然后查询处理继续,可能在等待更多数据到达时多次返回,最终完成操作。

该 API 经过精心设计,恰好满足了 node.js 等事件驱动框架方便高效地使用它的需求。因此,我非常高兴地发现 mariasql 项目将这一理论付诸实践。我当然尤其高兴看到基准测试,在涉及查询(以及因此涉及网络等待)时,非阻塞库明显快于其他 node.js 的 MySQL 绑定。如果没有适当的非阻塞 API,框架必须诉诸于生成后台线程并将阻塞操作延迟到这些后台线程;推测性能提升来自于避免线程间同步和上下文切换。当然,避免维护后台线程也简化了实现。

顺便提一下,MariaDB 非阻塞客户端库与所有版本的服务器完全兼容,包括所有 MySQL 版本。因此,虽然 mariasql 必须链接到 MariaDB 客户端库,但它可以与任何 MySQL 服务器一起使用(mariasql 源代码树似乎包含了 MariaDB 客户端库,以便用户更容易使用)。