OneProxy :: 在虚拟化和容器化时代,集中式连接池正变得前所未有地重要!

大约在2007年初,当时在全球最大的电子商务网站eBay做DBA,曾经惊心动魄地将部份Oracle数据库的最大连接数调到20000,调完后我们所有人基本上都不愿意登录到那些数据库上去做任何操作;后来据说最大连接数已经调到了50000个,当然也切换到了MTS模式,听到这么多会话数还是吓了一跳。 转眼一想,现在这么多公司在搞虚拟化,搞Docker化,完全有能力在几秒钟内给你布署1000个应用实例,问题是数据库怎么办呢?数据库连接怎么办呢?可能花个几个星期或几个月都不能够将数据切分到不同的机器上,因此数据库相对于应用程序总是集中在数台机器上。

在这里我们只讨论一下连接数的问题,不可否认应用端除PHP程序外都会有连接池功能,用来减轻数据库的压力。但随着应用布署量越来越多,在大型网站中,需要连接同一台数据库的应用实例数很有可能达到几百或上千个,每个应用实例的连接池中缓存10个连接,数据库上便会有几千或上万个连接。虽然软硬件现在都进步了不少,但学是不想面对上万个连接的数据库;好象也没有很好的算法可以完全随机和公平地地将请求打散到上千个应用实例中,因此每个应用的连接池都处于最大连接数。

client_side_connection_pool

针对数据库的连接数,好象没有什么特别的办法,除非使用独立布署的数据访问层,象OneProxy这样基于第7层协议的代理软件,允许不同的应用之间共享数据库的连接,才可以缓解大量应用实例到数据库之间暴涨的连接数。

proxy_side_connection_pool

从应用的角度来看,基于协议的代理软件相当于一台虚拟的MySQL服务器,应用并不需要直接和后端的MySQL数据库产生连接。只要在代理软件上建立连接池机制,就可以节约大量的连接;并且一个数据库服务器前面可以布署多台代理软件,使得应用可以放开连接数,安心地扩容,不再需要精心编制一个Excel表格,或者和开发讨价还价。OneProxy还可以接管数据库密码的登录验证,使得连接风暴也不会对后端数据库造成任何伤害。

在OneProxy中,实现了基于事务的连接池,连接会被保持直到事务结束;对于事务外的查询或更新,在SQL语句执行完后便会释放连接。即使分到不同应用的流量不够均匀,也不会引起数据库端真实连接数的增长。在连接池使用了后进先出的堆栈机制,可以减少数据库端线程的切换操作,以最大程度节约后端资源。对于超过5分钟(可以定制)没有任何操作的连接会进行回收,以减少真实的会话数。

连接池带来了上述的好处,也带来了一部份限制,所有的DBA和程序开发人员都应当知晓这些限制,原因是为了保证从连接池中取出任何连接时,其上下文环境是严格一致的,出于性能考虑工们禁用了一些可能会改变会话环境的功能。下面是比较重要的几点限制:

  1. 使用“use”命令来切换当前数据库, 出于性能考虑,我们不想在每次取得连接或归还连接时进行还原,这个操作需要一次额外的网络IO;如果要访问不同数据库下的表,请使用不同的用户进行连接(不同用户可提定不同的默认数据库),或者在表名前面加上库名进行访问(比如:db.tablename)。
  2. 除“set autocommit={0|1}”外的SET命令不会被发到后端执行,因为这些操作会影响会话的上下文,除非放在事务中(由开发人员自己负责)。
  3. 客户端的连接池仍有必要,可以节约应用服务器到OneProxy之间的网络调用, 从而提升API的响应时间,只是不需要太在意连接池的连接数配置了。
  4. 带传出参数的存贮过程调用,实际上是通过执行几句“SET”语句来声明会话级变量,以进行参数传递的,如果不在一个事务中,则不同的调用就不能保证使用的是同一个连接了(除非放在事务中),故而OneProxy默认禁止存贮过程调用。
  5. 不支持客户端级别和服务器端级别的Prepared接口,因为绑定变量及语句都是会话级别的。请不用担心,MySQL JDBC驱动本身就不是真正的Prepared接口的(除非显式指定了“useServerPrepStmts”参数);针对PHP PDO编程接口,请在连接数据库时指定“ATTR_EMULATE_PREPARES”选项,使用拼接SQL的方式进行底层交互(并不会影响安全性,增加SQL注放攻击的概率)。对于Web应用来讲连接池相对更重要,并且使用真正的Prepared接口需要更多的网络调用来完成一个SQL来回,并不利于性能。

在OneProxy里是无法禁用连接池功能的,会针对每个后端实例的每个MySQL用户创建一个连接池,并且会默认创建10个连接(可更改配置)。如果你在OneProxy后端挂了很多的MySQL服务器,或者你的连接数很大,请确保在启动OneProxy之间使用“ulimit -n 65536”命令来调节一下可以打开的最大文件数。可能通过OneProxy的管理端口来查看连接池的运行情况,如下所示:

mysql> list pool;
+------+----------------+------+--------+------+---------+---------+-----------+
| INDX | ADDRESS        | USER | LENGTH | SIZE | MINIDLE | MAXIDLE | REQUESTS  |
+------+----------------+------+--------+------+---------+---------+-----------+
|    1 | 127.0.0.1:3307 | test |      0 |    0 |      10 |     200 |         0 |
|    2 | 127.0.0.1:3305 | test |     10 |   10 |      10 |     200 | 154643502 |
+------+----------------+------+--------+------+---------+---------+-----------+
2 rows in set (0.00 sec)

OneProxy会每隔250毫秒去检查一下后端数据库的状态, 当检测到失败时,会自动销毁连接池中的连接,并将后端节点标记不不可用(markdown),同样以这个频率去检查数据库是否回来,并将后端节标记为可用状态(Markup)。在后端数据库状态切换的过程中,只有当前正在处理的会话会受到影响,如果没有任何请求,应用到OneProxy的连接并没有中断。