本文为您介绍PolarProxy(代理)的一致性级别、事务拆分、主库不接受读、连接池、Hint以及并行查询等功能。

PolarDB架构及PolarProxy介绍

PolarDB架构

PolarDB是一个由多个节点构成的数据库集群,一个主节点,多个读节点。对外默认提供两个地址,分别为主地址和集群地址。其中,集群地址功能由PolarProxy提供,集群地址分为只读和可读可写两种读写模式,可读可写模式支持读写分离,只读模式支持按连接数负载。读写分离请参见读写分离,更多关于PolarProxy的功能介绍请参见下文。

一致性级别

由于PolarDB使用异步复制的方式进行主从同步,该模式在读写分离模式下,可能出现一个写请求之后的读请求无法获得最新的写结果,导致不一致。PolarDB提供了如下几种一致性级别供您选择取值范围如下:

  • 最终一致性

    PolarDB采用异步物理复制方式在主库和只读库间做数据同步, 在主库更新后,相关的更新会同步到只读库,具体的延迟时间与写入压力有关, 延迟一般在毫秒(ms)级别, 通过异步复制的方式实现主库和只读库之间的最终数据一致。该级别无法保证业务通过读写分离地址获得一致性的数据。

  • 会话一致性(推荐)

    为解决最终一致性会出现的查询不一致,PolarDB利用自身物理复制速度快的优点,内部数据库代理会保证将查询发给已经更新了数据的只读节点,以满足会话一致性需求。

    说明 会话一致性只能满足一个会话内的请求是一致的,不能保证不同会话间的一致性,当所有只读节点都无法满足一致性要求时,会把该读请求路由到主库,所以可能引起主库读压力大。
  • 全局一致性

    PolarDB的读写分离模块提供了会话一致的功能,保证同一个会话内的请求能保证因果一致,但是部分应用场景,除了会话内部有逻辑上的因果依赖关系,会话之间也有依赖关系,例如短连接场景或者连接池,这时需要全局一致的功能。全局一致行是通过跟踪复制位点来实现的,在主从延迟较高时,可能会导致更多的请求路由到主库,造成主库压力大,同时业务延迟也可能增加。

说明 更多关于一致性级别的内容请参见PolarDB一致性级别

事务拆分

在默认的Read Commited隔离级别下,当数据库接收到开启事务的语句(例如begin或set autocommit=0)时,不会立即开启事务,而是在发生写操作时才正式开启事务。

通常情况数据库代理会将事务内的所有请求都发送到主节点,但由于某些框架实现的原因会将所有请求封装到事务中,导致主节点负载过大。此时您可以开启事务拆分功能,开启后数据库代理会识别当前事务的真正状态,将正式开启事务前的读请求通过负载均衡模块分流至只读节点。

事务拆分
说明
  • 读写分离模式下,事务拆分功能默认开启,如何修改事务拆分状态请参见高级选项-事务拆分
  • 某些业务对一致有较高要求,开启事务拆分后将不满足一致,因此在事务拆分前请充分评估事务拆分功能是否适用于您的业务。
  • 更改事务拆分设置后,仅对新建连接生效。

主库不接受读

当主库配置为不接受读时,普通的读请求将不再路由到主库。而事务内,一致性要求的读还是会被路由到主库,以保证业务的需求,另外当所有只读节点出现故障后读请求也会路由到主库。如果业务对一致的要求较低,可以通过设置一致性级别最终一致性来减少读请求到主库,也可以通过事务拆分功能来减少真正事务前的读请求路由到主库。 广播的请求(例如set, prepare)还是会路由到主库上。

说明
  • 读写分离模式下,主库不接受读默认开启。
  • 更改主库不接受读后立即生效。

连接池

PolarDB提供了会话级连接池和事务级连接池。

  • 会话级连接池

    会话级连接池适用于PHP短连接场景。

    PHP短连接优化用于减少短连接业务频繁建立新连接导致MySQL负载高。当您的连接断开时,系统会判断当前的连接是否是一个闲置的连接,如果是闲置连接,系统将会代理该连接并保留在连接池中一小段时间,如果这时新的连接建立的话就会直接从连接池里获得连接(命中的条件包括user, clientip, dbname等),从而减少与数据库的建连开销;如果没有可用的连接,则走正常连接流程,重新与数据库建立一个新的连接。

    说明 该优化并不能减少数据库的并发连接数,只是减少应用与数据库建连的速率,从而减少MySQL主线程的开销,能够更好的处理业务请求。但是连接池里空闲的连接会短暂占您的连接数。
  • 事务级连接池

    事务级连接池适用于总连接数比较多的场景,例如连接数上万的情况。

    事务级连接池主要用于减少业务的连接数,以及减少短连接场景下频繁新建连接带来的负载。客户端可以与代理建立大量连接,而同时代理到数据库只创建少量连接。当客户端发送连接请求时,代理将从连接池中选取符合条件的(现阶段为系统变量一致)连接进行请求发送到数据库,并在当前事务结束后将连接放入连接池。

    使用限制如下:

    • 当执行以下行为时,锁定连接,直至连接结束,即该连接不会再被放到连接池里供其它用户连接使用。
      • 执行prepare语句或命令
      • 创建临时表
      • 修改用户变量
      • 大报文(例如16M以上)
      • 使用lock table
      • 多语句
      • 存储过程调用
    • 不支持FOUND_ROWS、ROW_COUNT和LAST_INSERT_ID函数的调用,这些函数可以调用成功,但是结果无法保证正确。
    • 对于设置了wait_timeout的连接,wait_timeout在客户端的表现可能不会生效,因为每次请求都会从连接池中获取连接,当wait_timeout超时后,只有连接池中的后端连接会断开,而后端连接断开并不会导致客户端连接断开。
    • 除了sql_modecharacter_set_servercollation_servertime_zone这四个变量以外,如果业务依赖其他session级别的系统变量,那么需要客户端在建连之后显式进行set语句执行,否则连接池可能会复用系统变量已经被更改过的连接。
    • 由于连接可能会被复用,所以使用select connection_id()查询当前连接的thread id可能会变化。
    • 由于连接可能会被复用,所以show processlist中,显示的IP地址和端口可能和客户端实际的IP地址和端口不一致。
    • 数据库代理会将所有节点上的show processlist结果合并并返回,而在事务级连接池开启后,前端连接和后端连接的thread id无法对应。这导致kill命令会可能返回一个错误,但是实际上kill命令已经正常执行成功,可再通过show processlist确定相应的连接是否断开。
说明
  • 更改连接池设置后,仅对新建连接生效。
  • 当前连接池功能不支持同一个账号对不同IP有不同的权限。如果您为同一个账号的不同IP设置了不同的库或者表权限,开通连接池可能会导致权限错误问题。例如user@192.xx.xx.1设置了database_a的权限,而user@192.xx.xx.2没有database_a的权限,可能会导致连接复用时权限出错。
  • 该功能是指Proxy的连接池功能,不影响客户端的连接池功能,如果客户端已经支持连接池,则可以不使用Proxy的连接池功能。

Hint语法

在SQL语句前加上/*FORCE_MASTER*//*FORCE_SLAVE*/可以强制指定这条SQL的路由方向。

例如select * from test默认会路由到只读节点,改为/*FORCE_MASTER*/ select * from test就会路由到主节点。

说明
  • Hint的路由优化级最高,不受一致性级别和事务拆分的约束,使用前请进行评估。
  • Hint语句里不要有改变环境变量的语句,例如/*FORCE_SLAVE*/ set names utf8; 等,这类语句可能导致后续的业务出错。