伴随互联网、大数据、物联网、人工智能等技术的演进,万物互联已成为一种主流发展趋势,各行各业都在积极地将数字化融入到自身的发展中,大数据量、高并发量、高可用性、高一致性等业务场景成为常态。然而,越来越大的业务系统数据量以及随之变大的数据库操作开销,逐渐成为整个业务系统的性能瓶颈。
实现方案
常见方案解析
在大数据量、高并发场景下,已很难基于单节点继续提升性能。各厂商纷纷转向集群,将负载均衡的路由单节点驶向多节点,并给出不同的解决方案:
|
方案对比 |
优点 |
缺点 |
|
采用分布式架构 |
1 可以根据需要横向扩展 2 没有单点故障问题 |
1 需要结合业务特征改造使用分片键 2无法完整支持复杂查询和存储过程函数等 3 分布式事务性能较差 4 结构复杂,维护成本高 |
|
应用层负载均衡 |
1结构简单,方便理解 2业务可以直接控制分发行为 |
1需要应用层做读写分离和负载均衡,干扰原本业务; 2老业务改造成本较大; 3应用后期维护成本高 |
|
中间代理负载均衡 |
1对应用透明,业务无需改造 |
1多一次代理中转,有一定损耗 2 代理自身易变成瓶颈 3 多引入一层,增加维护成本 |
|
客户端编程接口侧负载均衡 |
1对应用透明,业务无需改造 2 驱动直接分发无损耗 3 没有额外引入,不增加维护成本 4多种分发和负载均衡策略,满足多种业务需求 |
1 依赖数据库驱动自身能力 |
这些方案适用于不同场景及业务,各有优劣。金仓的解决方案就属于上表中“客户端编程接口侧负载均衡”。
金仓方案
作为数据库厂商,金仓发布了多种解决方案来应对复杂且多变的业务需求,其中客户端编程接口侧负载均衡技术配合数据库读写分离集群应用于高并发业务场景,能显著提升系统的高可用性和整体吞吐量。
该方案通过在客户端编程接口内部实现根据业务SQL类型和当前事务状态把SQL路由到数据库集群中的不同节点,并保持对应用透明,最终达到负载均衡的访问数据库集群。金仓客户端编程接口内部直接识别语句类型和事务读写状态,把写语句发给主机执行,把读事务中的读语句负载均衡的分发给所有备机节点执行,以减轻单个数据库节点的负载压力,提高数据库集群整体的处理性能。

金仓采用嵌入自身客户端编程接口侧的、基于语句级的动态负载均衡技术,实现了对前端开放式应用请求的高效、智能负载均衡,既充分利用了数据库服务器的算力,又提高了数据库系统的并发处理能力,保证应用的访问质量。
(1) 将负载均衡算法嵌入在自身的客户端编程接口中,无需专门的路由节点,因此可避免路由操作本身带来的请求转发耗时,提高路由操作本身的效率,缩短应用请求的处理时间。

(2) 采用SQL语句级(而非数据库连接级)的细粒度路由单位,可适应多应用负载混合共用统一数据库、及应用负载动态变化的应用场景,保证请求路由分配的场景适应性。
SQL语句级路由单个会话执行效果演示:
|
Master |
S1 |
S2 |
S3 |
|
Session1_SQL1 |
|||
|
Session1_SQL2 |
|||
|
Session1_SQL3 |
|||
|
Session1_writesql |
|||
|
后续SQL直到事务结束 |
SQL语句级路由多个并发会话执行效果演示:
|
Master |
S1 |
S2 |
S3 |
|
Session1_SQL1 |
Session2_SQL1 |
Session3_SQL1 |
|
|
Session4_SQL1 |
Session1_SQL2 |
Session2_SQL2 |
|
|
Session3_SQL2 |
Session4_SQL2 |
Session1_SQL3 |
|
|
... |
(3) 采用基于CPU、内存、数据库连接数度量的数据库服务器实时负载情况的动态负载均衡算法,实现对数据库集群节点算力的均衡使用,确保请求路由分配的均衡性。
基于数据库实时负载的动态负载均衡执行效果演示:
|
实时负载 |
S1 |
S2 |
S3 |
|
三台CPU均衡都是50% |
100条/sec |
100条/sec |
100条/sec |
|
S1 CPU突然升高到100%,SQL执行变慢 |
0条/sec |
150条/sec |
150条/sec |
|
S1 30% S2 50% S3 70% |
140 条/sec |
100条/sec |
60条/sec |
技术揭秘
节点读写分工,缓解争用现象
在事务处理过程中,以下四种操作分别对应不同的节点执行:
- 如果事务中只有读操作,通过金仓客户端编程接口驱动将事务中的语句分发到Slave节点中执行。
- 如果事务中只有写操作,通过驱动分发器将整个事务内的所有语句都分发到Master节点中执行。
- 如果事务中开始是读,后续有写操作,则将开始的读操作分发到Slave节点中执行,直到写操作后,该事务中的所有SQL将在Master节点中执行。
- 对于无法识别读写类型的,将发送到Master节点中执行。
双重语句分发策略,满足不同业务需求
单语句事务:每条语句都是一个独立的事务,所以读语句分发备机没有问题。
多语句事务:读语句处于事务内,分发需要考虑事务隔离级别。
金仓数据库支持的三种隔离级别:可重复读,读提交,序列化。严格意义上这三种隔离级别均不允许读语句分发,因为可能出现不可重复读或者读不到已提交的内容。但如果事务内的语句就不分发的话,多个Slave节点就被浪费了。因为无论是应用还是框架基本上都是用事务控制的,因此金仓客户端编程接口提供了参数 TransactionDispatchStrategy 来控制事务内语句分发策略。
事务内语句分发策略有一下两种(默认是 2):
- 1 表示事务内的所有语句都只发主机,完全不分发备机;
- 2 表示事务内遇到写语句之前的读语句,可以分发备机,遇到写语句之后就只发主机。
读语句可分发节点选择策略,解决异步备机数据延迟问题
我们知道主备库实质是基于副本的,那么主备之间就可能存在数据差异,为此金仓提供同步复制的方式,客户端编程接口也对应提供控制查询语句的分发节点选择策略的参数(性能优先,默认是 1):
- 1 表示所有节点均可分发:仅看是不是可用的在线节点,是就可以分发,不考虑备机的数据延迟,最大化分发,负载效率最好,但可能出现备机数据延迟造成的主备读取数据不一致。属于弱一致性,适用不要求强一致的应用,比如历史数据分析。
- 2 表示只发主机同步备机:异步备机只做 HA,读语句可分发到主机和同步备机,最大化读取一致性,但无法利用异步备机负载,性能有损耗,属于强一致性,适用要求强一致的应用。
多个备机负载均衡,提升查询吞吐量
金仓客户端编程接口可通过参数控制读语句发给主机的比例,方便根据具体主备机器性能差异来分配负载。所有的Slave节点用于读操作。
当读取操作较多时,驱动分发器将通过多种负载均衡算法,比如基于语句执行数的负载,基于接数的负载,和基于响应时间的负载,将读操作均衡分布在所有有效的Slave节点中执行,降低Master节点负载,通过Slave节点负载均衡,提高查询性能。
定时心跳检测,出现脑裂及时止损
客户端编程接口侧负载均衡本意主要解决的是数据库接入层的高可用和负载均衡,对于脑裂无法直接解决。为此金仓客户端编程接口提供两重机制减少脑裂发生后双写造成的数据损失。
- 通过驱动内部的顺序检测机制,可以保证新建连接只会连接到第一个主机上,此时不会发生脑裂双写问题。
- 对于连接池中的已有连接,有可能已经在使用第二个主机,这部分连接认为自己还是有效的,此时可能发生双写。为此我们引入定时检测机制,判断一个集群内是否出现超过1个主机的情况,如果检测到脑裂发生,现有连接会被强制关闭,并抛出脑裂异常警告让业务人员可以及时干预。
功能影响
读写分离对事务隔离性的影响
|
所有节点均可分发 |
只发主机、同步备机 |
|
|
事务内只发主 |
满足 RP,不满足 RC |
满足 RP,满足 RC |
|
事务内写前分发 |
都不满足 |
不满足 RP,满足 RC |
读写分离对写后读一致性的影响
|
所有节点均可分发 |
只发主机、同步备机 |
|
|
单个事务内 |
满足 |
满足 |
|
并发事务间 |
不满足 |
满足 |




