所做的项目有个需求,有一个线程接收Udp请求然后向mysql的一个表插入一行记录.由于不希望该功能过多占用CPU,因此只用一个线程。 一开始用mysql-connector-c++作为客户端,由于只有单线程,插入速度不如人意。 主要是原因是网络延迟太大,线程大部分时间阻塞在等待数据库返回数据,于是考虑用异步的方式访问数据库。

目前mysql官方只有C#版本的connector有async操作的支持, mysql-connector-c/c++ 都没有提供异步操作的API. 但mariadb-connector-c提供了兼容mysqlnon-block API。 (该网页提到mysql-connector-c早就支持non-blocking,但没看出来怎么用,也找不到任何示例..)。

mariadb提供了一个使用异步API的demo,里面结合了libevent,最主要的部分是异步操作状态机的实现,看着比较费解, 我重新改写了,放在这里

以下是一些测试结果(被插入记录的表使用InnoDB存储引擎;mysql数据库在同一台主机上;测试代码见 blocking,non-blocking):

记录数 阻塞式 非阻塞式 时间比
1 0.031 0.029 1.1
100 3.011 0.624 4.8
10000 328.636 67.098 4.9

其中非阻塞式用了10个mysql连接。连接数增加时,耗时更少,几乎成反比(直到到达数据库处理能力的极限).

使用非阻塞式确实能够加快数据库操作,但存在问题:代码的复杂性增加了,特别是对于一些查询类操作,如果使用非阻塞操作的话,势必要维护每一个数据库请求的上下文数据. 很多情况下也可以通过使用阻塞式API但增加线程数(也即是数据库连接数)来解决这个问题,未必要使用非阻塞的API。

mariadb官网文档认为有以下场合比较适合使用非阻塞式API:

  • 需要获取多个数据库的数据以进行下一步操作,用异步API可以使这些数据库请求并行化。
  • 原有的代码使用了libevent等非阻塞式的框架

由于同一个连接无法同时处理多个查询,因此不管是阻塞还是非阻塞,连接数增加才有可能提高TPS。 当然,数据库的处理能力才是最根本的限制,在数据库处理能力内,可以通过选择合适的连接数来使数据库的能力达到最大值.但数据库处理能力跟不上的话,无论是阻塞式还是非阻塞,增加连接数也无法提高吞吐量。

回到我开始提到的需求来说,由于我只是插入记录,而且几乎不会有插入失败的情形,因此几乎不用维护上下文,可以使用这种异步的方式来操作数据库,既提高了处理速率,又不会产生太多的线程资源。

使用过程中发现mariadb-connect-c中,mysql连接关闭时没有清理使用异步mysql而额外申请的内存,顺便提了个pull request。