接着,明天咱们聊一个不常见的 Java 面试题:为什么数据库连接池不采纳 IO 多路复用?

这是一个十分好的问题。IO多路复用被视为是十分好的性能助力器。然而个别咱们在应用 DB 时,还是经常性采纳c3p0tomcat connection pool等技术来与 DB 连贯,哪怕整个程序曾经变成以Netty为外围。这到底是为什么?

首先纠正一个常见的误会。IO多路复用听下来如同是多个数据能够共享一个IO(socket连贯),实际上并非如此。「IO多路复用不是指多个服务共享一个连贯,而仅仅是指多个连贯的治理能够在同一过程」。在网络服务中,IO多路复用起的作用是「一次性把多个连贯的事件告诉业务代码解决」。至于这些事件的解决形式,到底是业务代码循环着解决、丢到队列里,还是交给线程池解决,由业务代码决定。

对于应用DB的程序来讲,不论应用多路复用,还是连接池,都要保护一组网络连接,反对并发的查问。

为什么并发查问肯定要应用多个连贯能力实现呢?因为DB个别是应用连贯作为Session治理的根本单元。在一个连贯中,SQL语句的执行必须是串行、同步的。这是因为对于每一个Session,DB都要保护一组状态来反对查问,比方事务隔离级别,以后Session的变量等。只有单Session内串行执行,能力保护查问的正确性(试想一下一组sql在一直的增减变量,而后这组sql乱序执行会产生什么)。保护这些状态须要消耗内存,同时也会耗费CPU和磁盘IO。这样,限度对DB的连接数,就是在限度对DB资源的耗费。

因而,对DB来说,要害是要限度连贯的数目。这个要求无论是DB连接池还是NIO的连贯治理都能做到。

这样问题就绕回来了,为什么DB连贯不能放到IO多路复用里一并执行吗?为啥大家都用连接池?

答案是,能够用IO多路复用——然而「应用JDBC不行」。JDBC是一个呈现了近20年的规范,它的设计外围是BIO(因为199X年时还没有别的IO能够用):调用者在通过JDBC时执行比方query这样的API,在没有执行实现之前,整个调用线程被卡住。而相似于Mysql Connector/J这样的driver齐备的实现了这套语义。

当然如果DB Client的协定的连贯解决和解析略微改一下:

  1. 将IO模式调整为Non-Blocking,这样就能够挂到IO多路复用的内核上(select、epoll、kqueue……)
  2. 在Non-Blocking实现的根底之上实现数据库协定的编码和解析

就能够实现用IO多路复用来拜访DB。实际上很多其余语言/框架里都是这么干的。比方 Nodejs,see https://github.com/sidorares/node-mysql2;或者 Vert.X 的 db 客户端https://github.com/mauricio/postgresql-async,不要在意这个名字,它实际上同时反对mysql和postgres)。只不过对于IO多路复用,数据库官网仿佛都没做这种反对——他们只反对JDBC、ODBC等等这些标准协议。

那么为什么基于 IO 多路复用的实现不能成为默认的,官网的,而要成为偏门呢?

对于数据库开发者来说。这种用法在整体的用户里占有量十分小,所以兴许不值当的花大力量。只须要把协定写分明(比方https://dev.mysql.com/doc/internals/en/client-server-protocol.html),就能够做实现。那么社区的有趣味的人天然就能够去做。

另外一个起因是体系的反对。简略来讲,如果没有一个大的 Reactive 的运行环境,IO 多路复用的应用会十分受限。

IO 多路复用之所以能成立,是须要「整个程序要有一个IO多路复用的驱动代码」——就是 select 那句调用——期待事件降临,一个 blocking 的 API。整个程序必须以这个驱动代码为外围。这样就对整个代码的构造产生重大的影响。这种影响是没法用简略的接口形象的。

Java Web 容器之所以能够应用 NIO 是因为 NIO 能够被封装到容器外部。Web 容器对外裸露的还是传统的多线程模式的Java EE接口。

如果 DB 和 Web 容器同时应用 NIO,那么调用的DB连贯库与必须与容器有一个约定形容「DB的连贯治理如何接入Web容器的NIO的驱动代码」。在 Java 这个大环境下,不同人,不同的容器写的代码不同;又或者,不应用任何常见的容器,而是本人用 NIO 去封装一个。这样是无奈造成代码上的约定的。那么多个独立的组件就不能很好的共享 NIO 的驱动代码。

下面这个用法假如整个程序应该共享一个 NIO 驱动代码。那么 Web 和 DB 可不可以各用各的呢?也是能够的,然而为了保障这两个 NIO 驱动代码不会互相 block,最好要离开两个线程。这样一来就会突破个别 Web 服务一个申请解决用一个线程的个别做法,会让程序边的更简单——你的业务代码和DB查问之间必须做跨线程数据交换。

相同,连接池的实现就绝对独立的多,也简略的多。外界只有配好 DB URL,用户名明码和连接池的容量参数,就能够做到自行治理连贯。

NodejsVert.X是齐全不同的。他们实质就是Reactive的。他们的NIO的驱动形式是其运行时的根底——所有要在这个根底上开发的代码都必须恪守同样的NIO+异步开发标准,应用同一个NIO的驱动。这样DBNIO的合作就不成问题了。

最初,「有大量场景是须要BIO的DB查问反对的」。批处理数据分析代码都是这样的场景。这样的程序写成NIO就会得失相当——代码不容易懂,也没有任何效率上的劣势。相似于Nodejs这样的运行时在此场景下,反而要利用async或等价的语法来让代码看起来是同步的,这样才容易写。

总结一下。DB 拜访个别采纳连接池这种景象是生态造成的。历史上的 BIO + 连接池的做法通过多年的倒退,曾经解决了次要的问题。在 Java 的大环境下,这个计划是十分靠谱的,成熟的。而基于 IO 多路复用的形式只管在性能上可能有劣势,然而其对整个程序的代码构造要求过多,过于简单。当然,如果有特定的须要,心愿应用 IO 多路复用治理 DB 连贯,是齐全可行的。