本文由 Jilen 发表在 ScalaCool 团队博客。mysql
Mysql Async 是一个 Scala 编写的,基于 Netty 实现的非阻塞异步数据库驱动。在本系列文章中咱们将逐步分析:git
项目官网设计目标以下github
Future
能够看出做者是但愿经过异步非阻塞能让驱动更快(注意此处咱们不讨论是真异步或者伪异步)。
接下来本文将具体分析与传统的 mysql-connector/j
相比到底是不是更快,快在哪里。sql
synchronized
和 Lock
mysql-connector/j
使用的仍是 Blocking IO ,这要求处理请求时必需有足够多的线程,不然吞吐量将受很大限制。数据库
例如一样基于 Blocking IO 的 Tomcat7
默认就配置了 200 线程。编程
项目还提供一个链接池,采用分区设计,一个 PartitionedAsyncObjectPool
包含多个 SingleThreadedAsyncObjectPool
。网络
流程十分简单,根据线程的 id 选择 SingleThreadedAsyncObjectPool
,而后从中获取数据库连接。不存在阻塞的可能并发
顾名思义,这是一个单线程的对象池。当请求获取连接时,若是有多余连接则直接返回,若是没有则加入队列,等待有连接经过 giveBack
方法释放时返回给队列里的某个请求。
这里用了 Scala 的 Future
和 Promise
实现,也不存在阻塞的状况。框架
分析源代码后发现此处使用只有一个线程的 ThreadPoolExecutor
来确保同一时间只有一个线程请求连接。异步
// Worker.scala
def action(f: => Unit) {
this.executionContext.execute(new Runnable {
def run() {
...
}
})
}复制代码
上述代码中this.executionContext.execute
最终会执行 TreadPoolExecutor.execute
而 TreadPoolExecutor.execute
并非彻底非阻塞的。
这带来了一个问题:当多个线程同时要获取连接时,只有一个线程能够得到连接,其余线程所有处于 blocked
状态。
因为是分区设计,而且 Play 这样的全异步框架主线程数默认很是少,因此这个问题在某些场合下并不严重。
HikariCP 也许是目前优化得最好 JDBC 链接池。
该项目 Wiki 中的几篇文章也值得一看。
咱们没法从理论上直接得出何者性能更优的答案,后续将经过具体测试来估计何者更优。
为了验证上述观点,我进行了简单的性能测试,主要测试了简单查询和事务两个方面。
SELECT 1复制代码
update user set remain = remain + ? where id = ?
update user set remain = remain - ? where id = ?复制代码
Mysql Async
有微弱的优点。hikaricp + mysql-connector/j
方案也许能够提高性能,但这套方案的问题是你永远不知道多少线程和连接数才是合适的。下表是结合上述测试和定性分析得出的结果
项目 | MysqlAsync | HikariCP + mysql-connector/j |
---|---|---|
编程模型 | 异步 | 同步 |
网络IO | NIO | BIO |
连接池 | 异步实现 | 同步实现 |
过载防御 | 经过调节队列长度实现 | 须要额外实现 (例如指定线程池任务队列长度) |
可伸缩性 | 只须要设置合理链接数(例如几十个) | 须要测试最佳线程数和连接数 |
线程数 | 少 | 多 |
总得来讲 MysqlAsync 经过减小了线程数确实达到了如下效果