2.3 读写分离
3.20.10.0版本dble支持单纯的读写分离,可以和分库分表功能分开单独使用。3.20.10.0之前的版本,分库分表也支持读写分离,兼容该功能。
2.3.1 读写分离配置
2.3.1.1 单纯使用读写分离功能的配置
若想启用dble的读写分离,仅需在 user.xml 文件中配置 rwSplitUser并指定对应的dbGroup即可。dbGroup的配置参考db.xml的章节。这里需要注意的是三种用户配置的顺序是固定的。user.xml的配置请参考user.xml章节。
<dble:user xmlns:dble="http://dble.cloud/" version="4.0">
<managerUser name="man1" password="654321" maxCon="100"/>
<shardingUser name="root" password="123456" schemas="testdb" readOnly="false" maxCon="20"/>
<rwSplitUser name="rwsu1" password="123456" dbGroup="rwGroup" maxCon="20"/>
</dble:user>
配置注意事项:
- 当user.xml文件中不配置shardingUser,dble不再加载sharding.xml配置文件(即dble不具备分表分库),包括集群情况下出现sharding.xml不一致,均属于已知现象。
- 当同时开启dble读写分离和分库分表的功能,分库分表引用的dbGroup和读写分离引用的dbGroup必须相互独立。rwSplitUser引用的dbGroup,仅需在db.xml中定义即可。shardingUser引用的dbGroup,需要被配置的schemas对应的sharding.xml中的shardingNode所引用。
- 多个rwSplitUser可以引用同一个dbGroup。
- 被读写分离或者分库分表使用的dbGroup内的instance才会有心跳和连接池;未被有效使用的dbGroup内的instance只有心跳,不会初始化连接池。
2.3.1.2 分库分表中读写分离的配置
分库分表中的读写分离,配置好db.xml和sharding.xml即可,具体参考db.xml和sharding.xml的章节
2.3.2 负载均衡
dble通过配置多个dbInstance为读操作提供负载均衡,注意的是rwSplitMode配置不为0,详细请参见db.xml章节。负载均衡规则如下:
- 确定参与读写分离的dbInstance集合
- 负载均衡算法
2.3.2.1 确定参与读写分离的dbInstance集合
该算法在每次连接获取时提供可用的dbInstances实例集。可用的dbInstance是指心跳正常的dbInstance,这里需要注意的是show slave status和其他心跳语句是有区别的,以该语句作为心跳语句,心跳正常只是基本前提。dble会根据最近一次的心跳返回结果判断读库和主库的延迟,如果延迟超过delayThreshold配置,则不会将此节点加入到dbInstances实例集中,如果delayThreshold=-1那么不会进行延迟检测。
- 写节点(primary="true")可用
- rwSplitMod配置为2,则写节点有资格参到读写分离,将写节点加入到dbInstances实例集
- 读节点(primary没配置或者 primary="false")
- 节点可用且需要进行延迟检测,检查延迟是否在阈值内再决定是否加入到dbInstances实例集
- 节点可用且不需要进行延迟检测,直接加入到dbInstances实例集
- 写节点异常
- 检查读节点是否可用,与上面读节点的检测机制一致
2.3.2.2 负载均衡算法
该算法在dbInstance集合中选择一个dbInstance实例来获取连接。
- dbInstance集合为空,前端报错。
- dbInstance集合非空
- 每个dbInstance有权重设置(readWeight参数), 但不都是等值权重, 依权重随机选择。
- 每个dbInstance无权重设置或所有权重等值, 则等权随机选择。此种情况只是上面情况的特例。
2.3.2.3 写节点是否参与均衡与dbGroup的rwSplitMode属性有关,具体见下图
2.3.3 读写分离支持语句类型
在事务中所有语句都会发主,在非事务中则根据语句类型进行负载均衡。
2.3.3.1 纯读写分离支持的语句类型
- ddl
- dml
- prepared statement协议
- 函数,存储过程
2.3.3.2 分库分表支持的语句类型
- 可进行负载均衡的SQL语句为 select 或者 show。
2.3.4 读写分离功能限制
- druid 解析器限制 - 不支持set语句中存在特殊字符;
- druid 解析器限制 - set session transaction read write, isolation level repeatable read中,逗号后的语句不生效;
- 只读事务(在 >= dble 3.21.06.x版本中支持,之前的版本不支持此功能)
- 不支持set transaction read write;
- select 语句现在的逻辑是都进行负载,还没有进行细节的区分,比如有些语句需要强制发主,如系统函数,系统表,系统变量;
- select ... into 或者 load data中存在用户变量,通过dble再次查询该变量,变量值不对;
- 在会话中,删除正在使用的库,mysql会将当前库置为null,dble会依然保留正在使用的库;
- set 语句目前只支持会话级别系统变量和用户变量的设置,若需要设置密码等可以使用hint的方式设置或去后端节点去设置;
- 读写分离会打破原先的隔离级别的原有语义,对此有严格要求的需要酌情考虑;
- 创建临时表后,之后所有的语句都发往主,因为临时表不支持主从复制。直到你删除了所有临时表后,原先的负载均衡策略恢复;
- 部分客户端,比如 在设置了 allowMultiQueries=true (默认为 false) 的 jdbc,此时客户端可以一次性发送 multi-queries , dble 对此情况不做拆分,全部发往主。MySQL Command-Line client 会在客户端拆分语句,一次只发送一条语句,故不会有该条限制 ;
2.3.5 读写分离后端实例的粘滞性
功能背景
读写分离非事务场景下, 写完立刻读, 读会发到从机上可能存在主从延迟从而导致读不到数据.
粘滞性
执行当前读SQL的时间,距离上一次写SQL执行的时间段,没有超过rwStickyTime时间段时,则当前读SQL将会下发至后端主(写)实例.
特例:Hint SQL不参与实例的粘滞性
特性
此粘滞性功能,优先于db.xml中的rwSplitMode配置
举例说明
假设,rwStickyTime=1000,表示粘滞时间段为1000ms;操作如下:
Step | Time Line | SQL | InstanceDB of backend | 说明 |
---|---|---|---|---|
0 | 50ms | Hint_SQL_1(如:/*master*/ sql) | master | 下发到主实例;不参与粘滞,不更新时间点timeA |
1 | 100ms | 写SQL_1 | master | 下发到主实例;更新时间点timeA=100ms |
2 | 500ms | 读SQL_2 | master | 原本应该下发到从实例,但满足rwStickyTime>0且(500ms-timeA)<=rwStickyTime,因此SQL最终下发到主实例 |
3 | 600ms | Hint_SQL_2(如:/*slave*/ sql) | slave | 依旧下发到从实例;不参与粘滞 |
4 | 900ms | 读SQL_3 | master | 原本应该下发到从实例,但满足rwStickyTime>0且(900ms-timeA)<=rwStickyTime,因此SQL最终下发到主实例 |
5 | 2000ms | 读SQL_4 | slave | 下发到从实例;(并不满足rwStickyTime>0&&(2000ms-timeA)<=rwStickyTime) |
补充:在读写分离中,读SQL和写SQL的定义较为简明:select...
、show ...
语句为读SQL,其他语句即为写SQL.
2.3.6 读写分离本地读
功能背景
读写分离本地读场景下,dble实例的配置与instance实例的配置一致,就可以认定该instance为本地实例,读流量会优先下发到本地实例,如果本地实例存在异常或者不可用,按照策略下发到其他实例
2.3.6.1 使用读写分离本地读的的配置
若想启用dble的读写分离本地读功能,需要在bootstrap.cnf和db.xml文件中配置
bootstrap.cnf
-Ddistrict=district1
-DdataCenter=dataCenterA
db.xml
<?xml version="1.0"?>
<dble:db xmlns:dble="http://dble.cloud/">
<dbGroup name="dbGroup1" rwSplitMode="1" delayThreshold="10000">
<heartbeat errorRetryCount="1" timeout="10" keepAlive="60" >show slave status</heartbeat>
<dbInstance name="instanceM1" url="ip4:3306" user="your_user" password="your_psw" maxCon="200" minCon="50" dbDistrict="district1" dbDataCenter="dataCenterA" primary="true">
</dbInstance>
<!-- can have multi read instances -->
<dbInstance name="instanceS1" url="ip5:3306" user="your_user" password="your_psw" maxCon="200" minCon="50" dbDistrict="district1" dbDataCenter="dataCenterA" primary="false">
</dbInstance>
<dbInstance name="instanceS2" url="ip6:3306" user="your_user" password="your_psw" maxCon="200" minCon="50" dbDistrict="district1" dbDataCenter="dataCenterA" primary="false">
</dbInstance>
<dbInstance name="instanceS3" url="ip7:3306" user="your_user" password="your_psw" maxCon="200" minCon="50" dbDistrict="district1" dbDataCenter="dataCenterB" primary="false">
</dbInstance>
<dbInstance name="instanceS4" url="ip8:3306" user="your_user" password="your_psw" maxCon="200" minCon="50" dbDistrict="district2" dbDataCenter="dataCenterC" primary="false">
</dbInstance>
</dbGroup>
</dble:db>
配置注意事项:
- 只有select语句生效
- rwSplitMode具有更高优先级
- 使用该功能至少需要bootstrap.cnf中配置district参数,对应的dbinstance配置dbDistrict参数
- 本地读匹配优先级
4.1 从district标签和dataCenter标签都符合的instance中挑选下发语句
4.2 找不到符合4.1中的条件或者符合4.1中条件的instance都处于异常状态,从只符合district标签匹配的instance中挑选下发语句
4.3 找不到符合4.2中的条件或者符合4.2中条件的instance都处于异常状态,从剩余的instance中挑选下发语句 - hint语句按照hint的写法下发
举例说明: dble使用如上配置,使用dbGroup1的读写分离用户下发一条select语句