连接池概述
数据库代理支持两种连接池:事务级连接池和会话级连接池。默认使用事务级连接池。两种连接池都能降低业务与数据库建立连接的频率,从而减少数据库建立连接带来的开销。
两者的区别在于连接复用的程度。数据库代理中连接分为前端连接和后端连接,前端连接时业务应用到数据库代理的连接。后端连接是指数据库代理到数据库实例的连接,这里连接池是特指后端连接的连接池。
当客户端发起连接会话请求时,只会与数据库代理建立前端连接,代理不会马上将其与后端数据库建立连接。当真正提交业务请求时,才会从连接池中查找可用的后端连接或者创建后端连接。
事务级连接池
应用场景
- 业务多为短连接,连接建立频繁;
- 连接数量很大。
工作原理
选择事务级连接池,当需要处理一个业务事务时,从连接池中查找是否存在可用的连接。如果存在则占用该空闲连接,否则代理与后端数据库创建一个新的后端连接。
在当前事务结束后将该连接放回连接池中。这样连接会被下一个事务使用,这个事务可能是当前业务会话,也可能是其他业务会话。
如上图所示:有事务的会话占用后端连接,没有事务的会话不占用连接,一个连接被多个会话轮流共用。因此它能显著减少后端数据的连接数量和连接频率,避免了空闲连接带来的资源浪费,特别适用于大量短连接的业务应用。
特别注意,事务级连接会在一些特殊的使用场景会转换为会话级连接:
- 执行锁操作LOCK TABLE, LOCK TABLES 或者FLUSH TABLES WITH READ LOCK;
- 执行了PREPARE 语句;
- 使用了TEMPORARY TABLE;
- 设置了SQL_LOG_BIN=0;
- 使用sql_safe_updates ,sql_select_limit ,max_join_size 等safe-updates设置;
- 使用了SQL_CALC_FOUND_ROWS 功能;
- 设置了FOREIGN_KEY_CHECKS ,UNIQUE_CHECKS,AUTO_INCREMENT_INCREMENT AUTO_INCREMENT_OFFSET ,GROUP_CONCAT_MAX_LEN 等等;
- SQL语句中包含了@ 字符。
会话级连接池
应用场景
- 业务多为长连接,连接建立不频繁
- 连接数量不是很大
工作原理
选择会话级连接池后,当需要处理第一个会话事务时,从连接池中查找是否存在未绑定的空闲连接。如果存在则绑定空闲连接,否则代理与后端数据库创建一个新的后端连接。
在当前事务结束后将仍然被会话占用,直到会话结束才会释放回线程池。此时才能被下一个会话复用。
如上图所示: 一个会话独占了专用后端连接,后端连接数量与会话并发数是正相关的,无论会话活跃与否。它的好处是会话始终保持很好响应速度,请求更稳定,适用于长连接的业务场景。
需要注意:
- 代理默认开启读写分离,读写事务连接到主实例,只读事务会连接到只读实例;
- 存在一个会话独占多个后端连接(分别到主实例和只读实例的连接);
- 业务需要评估应用特征是否为长连接,会话数量是否在可控范围内,是否超过单单实例的连接限制。
事务拆分
数据库代理提供了事务拆分的能力,开启事务拆分后,同一个事务中的读写SQL请求进行了路由拆分,写请求路由到主实例,读请求路由到从实例。
工作原理
数据库代理具备读写分离的能力,写请求路由到主实例,读请求路由到从实例。这样能充分利用从实例的资源,减小主实例负载。
一般情况下,业务应用使用自动提交的模式,即每一个SQL都是单独的事务。读写分离所起到的效果比较显著。
但有些业务框架会关闭autocommit,业务读写SQL需要显式commit,这样同时包含读写SQL的事务请求都会路由到主实例上。
如上图所示,包含读写SQL的事务,请求都路由到主实例上。如果业务存在大量读写事务,会导致主实例负载高而从实例负载低的情况。
为了应对这种情况,数据库代理提供了事务拆分的选项,业务无需改造应用代码,实现读写实例的负载均衡。开启后事务中的读写SQL路由进行了拆分,写请求路由到主实例,读请求路由到从实例,保留了读写分离的效果。
如上图所示,读请求都路由到了从实例,写请求路由到了主实例。这样读写分离规则在事务内部中仍然起作用。
特别说明:
- 数据库代理的事务拆分是默认关闭的;
- 一般情况下主从实例的延迟非常低,业务请求慢于主从同步,但仍然有一定概率读不到刚提交的数据。如果业务需要非常强一致性的场景,不建议启用事务拆分。