Logo
双路由

双路由 #

SphereEx-DBPlusEngine 包括 Driver 和 Proxy 两种接入形态,在复杂的业务场景下,选择混合部署架构会更为合适,即混合部署 DBPlusEngine-Driver 和 DBPlusEngine-Proxy,并采用统一的注册中心来配置分片策略,灵活地搭建出适用于各种场景的应用系统。

为了提升应用程序的查询性能和稳定性,我们可以考虑将耗资源较多的 SQL转发至独立服务形态的 DBPlusEngine-Proxy 中,由 DBPlusEngine-Proxy 负责计查询结果,再统一返回给应用程序。

概述 #

SphereEx-DBPlusEngine 的双路由插件允许用户根据 SQL 特点进行路由配置,耗资源的 SQL 可由 DBPlusEngine-Proxy 专门负责处理,充分发挥两种接入端所长。双路由插件会透明化 SQL 转发所带来的影响,让用户像使用一个数据库一样使用 DBPlusEngine-Driver 和 DBPlusEngine-Proxy 混合部署集群。

  • DBPlusEngine-Driver

DBPlusEngine-Driver 采用无中心化架构,与应用程序共享资源,适用于 Java 开发的高性能的轻量级 OLTP 应用,但是由于 DBPlusEngine-Driver 与应用程序共享资源,当执行消耗资源较多的 SQL 时,应用程序的稳定性和性能会受到影响。同时 DBPlusEngine-Driver 消耗的连接数较多,当应用程序和数据库部署在不同的网络分区时,网络延迟对于性能的影响也会更加明显。

  • DBPlusEngine-Proxy

DBPlusEngine-Proxy 提供统一的静态入口,并且独立于应用程序部署,适用于 OLAP 应用以及对分片数据库进行管理和运维的场景。用户使用 DBPlusEngine-Proxy 来执行资源消耗较多的 SQL,可以有效避免对应用程序产生影响。

基本概念 #

标签 #

DBPlusEngine-Proxy 实例配置的标签属性,用于对实例进行区分,DBPlusEngine-Driver 开启双路由功能时,转发的目标就是标签对应的 DBPlusEngine-Proxy 实例。

转发策略 #

对 DBPlusEngine-Driver 接入端的 SQL 进行转发的策略,包括了:目标 Proxy 实例标签、转发算法、负载均衡算法。根据算法的不同,可以将转发策略分为事务转发策略和普通转发策略,当策略中的算法配置为 TransactionTrafficAlgorithm 时,转发策略为事务转发策略,当策略中的算法配置为其他算法时,转发策略为普通转发策略。

转发算法 #

用于判断当前 SQL 是否需要转发的算法,包括了 HintTrafficAlgorithmSegmentTrafficAlgorithmTransactionTrafficAlgorithm

  • HintTrafficAlgorithm 基于 SQL Hint 功能进行 SQL 转发。

  • SegmentTrafficAlgorithm 则基于 SQL 语句进行转发,内部提供了基于 SQL 字符串以及 SQL 正则匹配的转发算法。

  • TransactionTrafficAlgorithm 是专门用于处理开启事务时,事务中的 SQL 语句如何进行转发的算法,目前支持 FIRST_SQL、JDBC 和 Proxy 三种策略。FIRST_SQL 表示基于事务语句中的第一条 SQL 转发结果,对事务中的 SQL 语句进行转发。JDBC 表示事务语句的 SQL 全部转发至 JDBC 接入端执行。Proxy 表示事务语句的 SQL 全部转发至 Proxy 接入端执行。

负载均衡算法 #

通过负载均衡算法将 SQL 语句转发到标签对应的不同 Proxy 实例上执行,目前支持 RANDOMROUND_ROBINWEIGHT 三种负载均衡算法。

算法类型的详情,请参见内置算法 - 负载均衡算法

适用场景 #

混合部署 DBPlusEngine-Driver 和 DBPlusEngine-Proxy,需要将消耗资源较多的 SQL 转发到 DBPlusEngine-Proxy 执行的场景。

使用前提 #

  • 双路由功能需要使用混合部署架构,同时部署 DBPlusEngine-Driver 和 DBPlusEngine-Proxy;
  • 需部署 ZooKeeper ,混合架构将通过注册中心(ZooKeeper )统一管理。

使用限制 #

  • 用户配置的内核功能不支持的 SQL,转发之后 SQL 仍然不支持;
  • 不支持开启事务后将 SQL 转发到不同的接入端或 Proxy 实例执行;
  • 只允许在 DBPlusEngine-Proxy 接入端通过 YAML 配置或者 DistSQL 来添加路由规则。

注意事项 #

虽然通过将 DBPlusEngine-Driver 接入端消耗资源较多的 SQL 转发到 DBPlusEngine-Proxy 执行,可以有效提升应用程序的性能和稳定性,但是这也导致了部署架构更加复杂,用户需要判断哪些语句需要转发到 DBPlusEngine-Proxy,并在 DAO 层开发相关的业务逻辑来控制 SQL 的转发。

此外,在开启事务的场景下,将事务中的部分 SQL 转发到 DBPlusEngine-Proxy 执行,会对事务的一致性和可见性带来影响,从而影响业务系统对于事务的使用。

原理介绍 #

Traffic Rule 用于判断当前执行的 SQL 语句是否需要转发,内部会支持多种转发算法。下面展示了 Traffic Rule 配置示例,trafficStrategies 用于声明请求转发策略,当用户配置了多个转发策略时,会按照配置的顺序依次去匹配,第一个匹配成功的策略将用于请求转发处理。

转发策略包含三个属性:

  • labels:请求转发 Proxy 对应的标签,支持配置多个标签,并且同一个标签也可能存在多个 Proxy 实例;
  • algorithmName:请求转发匹配算法,例如匹配操作类型是 select,并且包含了 group by 的语句,匹配成功则根据 labels 查找转发的 Proxy 实例;
  • loadBalancerName:用于指定转发的负载均衡策略,因为 labels 对应的 Proxy 实例可能包含多个,需要通过负载均衡策略决定转发到哪个实例上。

为了配合 Traffic Rule 中的 labels 属性,需要 Proxy 接入端支持 label 属性设置,并且能够将 Proxy 实例对应的 ip 和端口信息存储下来。

由于使用了双路由功能,原先在 JDBC 接入端执行的 SQL,会因为匹配转发策略,而被转发到 Proxy 接入端执行。在这种场景下,同一个事务单元中的语句,可能部分在 JDBC 接入端执行,部分在 Proxy 接入端执行,无法保证事务的一致性。

为了解决事务一致性的问题,我们在转发策略中增加了事务转发策略,事务转发策略的配置中包含 FIRST_SQL、JDBC、PROXY 三种类型事务匹配算法,FIRST_SQL 会根据第一条 SQL 转发的结果来决定事务单元的转发结果,JDBC 会将事务单元不进行转发,在 JDBC 上执行执行,而 PROXY 会将事务单元转发到 Proxy 实例执行,为了保证数据的一致性,事务单元会在同一个实例上执行。

Traffic Rule 内部在初始化路由策略时,会根据是否针对事务场景,将策略进行分类维护,针对事务场景的策略,只有在开启事务时使用,否则使用其他非事务策略。

使用指南 #

  1. 确认当前系统中消耗资源较高的 SQL,如聚合类语句,提取具体语句信息。可通过 SphereEx-Console 或其他监控工具来捕获。
  2. 在 DBPlusEngine-Proxy 中,通过 DistSQL 完成指定 SQL 的配置。
  3. 在 DBPlusEngine-Proxy 中开启 SQL 显示功能,业务发起新的请求,确认指定 SQL 的路由情况。

操作指南 #

  1. 确认需要转发到 Proxy 运行的 SQL 语句;
  2. 在 Proxy 中,通过 DistSQL 完成双路由规则的配置;
  3. 在 Proxy 中开启 SQL 展示;
  4. 应用测发起请求,通过 Proxy 日志确认 SQL;
  5. 确认结果无误后,在 Proxy 中关闭 SQL 展示;

配置示例 #

环境说明 #

实例IP 地址服务端口主机名备注
1DBPlusEngine 1.2.0192.168.xx.1023307dbplusengine
2Zookeeper 3.6.3192.168.xx.1022181dbplusengine
3MySQL 8.0.28192.168.xx.1033306mysql_0
4MySQL 8.0.28192.168.xx.1043306mysql_1

拓扑图 #

配置过程 #

  1. 确认需要转发到 Proxy 运行的 SQL 语句

  2. 配置双路由规则

mysql> CREATE TRAFFIC RULE sql_match_traffic (
    LABELS(OLTP),
    TRAFFIC_ALGORITHM(TYPE(NAME=SQL_MATCH,PROPERTIES("sql" = "SELECT * FROM t_order WHERE order_id = ?; UPDATE t_order SET order_id = ?;"))),
    LOAD_BALANCER(TYPE(NAME=RANDOM, PROPERTIES("key"="value")))
), sql_hint_traffic_1( 
    TRAFFIC_ALGORITHM(TYPE(NAME= SQL_HINT)),
    LOAD_BALANCER(TYPE(NAME=ROUND_ROBIN, PROPERTIES("key"="value")))
), sql_hint_traffic_2( 
    LABELS(OLAP, order_by),
    TRAFFIC_ALGORITHM(TYPE(NAME= SQL_HINT))
), sql_hint_traffic_3( 
    TRAFFIC_ALGORITHM(TYPE(NAME= SQL_HINT))
);

 mysql> SHOW TRAFFIC RULES;
  1. 在 Proxy 中开启 SQL 展示
mysql> SET VARIABLE sql_show = true;
Query OK, 0 rows affected (0.05 sec)

mysql> SHOW ALL VARIABLES;
+---------------------------------------+----------------+
| variable_name                         | variable_value |
+---------------------------------------+----------------+
| sql_show                              | true           |
| sql_simple                            | false          |
| kernel_executor_size                  | 0              |
| max_connections_size_per_query        | 1              |
| check_table_metadata_enabled          | false          |
| sql_federation_enabled                | true           |
| proxy_frontend_database_protocol_type |                |
| proxy_frontend_flush_threshold        | 128            |
| proxy_hint_enabled                    | false          |
| proxy_backend_query_fetch_size        | -1             |
| proxy_frontend_executor_size          | 0              |
| proxy_backend_executor_suitable       | OLAP           |
| proxy_frontend_max_connections        | 0              |
| proxy_backend_driver_type             | JDBC           |
| proxy_mysql_default_version           | 5.7.22         |
| proxy_default_port                    | 3307           |
| proxy_netty_backlog                   | 1024           |
| proxy_instance_type                   | Proxy          |
| agent_plugins_enabled                 | true           |
| cached_connections                    | 0              |
| transaction_type                      | LOCAL          |
+---------------------------------------+----------------+
21 rows in set (0.01 sec)
  1. 应用测发起请求,通过 Proxy 日志确认 SQL

  2. 在 Proxy 中关闭 SQL 展示

mysql> SET VARIABLE sql_show = false;
Query OK, 0 rows affected (0.01 sec)

mysql> SHOW ALL VARIABLES;
+---------------------------------------+----------------+
| variable_name                         | variable_value |
+---------------------------------------+----------------+
| sql_show                              | false          |
| sql_simple                            | false          |
| kernel_executor_size                  | 0              |
| max_connections_size_per_query        | 1              |
| check_table_metadata_enabled          | false          |
| sql_federation_enabled                | true           |
| proxy_frontend_database_protocol_type |                |
| proxy_frontend_flush_threshold        | 128            |
| proxy_hint_enabled                    | false          |
| proxy_backend_query_fetch_size        | -1             |
| proxy_frontend_executor_size          | 0              |
| proxy_backend_executor_suitable       | OLAP           |
| proxy_frontend_max_connections        | 0              |
| proxy_backend_driver_type             | JDBC           |
| proxy_mysql_default_version           | 5.7.22         |
| proxy_default_port                    | 3307           |
| proxy_netty_backlog                   | 1024           |
| proxy_instance_type                   | Proxy          |
| agent_plugins_enabled                 | true           |
| cached_connections                    | 0              |
| transaction_type                      | LOCAL          |
+---------------------------------------+----------------+
21 rows in set (0.00 sec)

语法和参数 #

创建&删除规则 #

创建双路由规则:

CREATE TRAFFIC RULE sql_match_traffic (
    LABELS(OLTP),
    TRAFFIC_ALGORITHM(TYPE(NAME=SQL_MATCH,PROPERTIES("sql" = "SELECT * FROM t_order WHERE order_id = ?; UPDATE t_order SET order_id = ?;"))),
    LOAD_BALANCER(TYPE(NAME=RANDOM, PROPERTIES("key"="value")))
), sql_hint_traffic_1( 
    TRAFFIC_ALGORITHM(TYPE(NAME= SQL_HINT)),
    LOAD_BALANCER(TYPE(NAME=ROUND_ROBIN, PROPERTIES("key"="value")))
), sql_hint_traffic_2( 
    LABELS(OLAP, order_by),
    TRAFFIC_ALGORITHM(TYPE(NAME= SQL_HINT))
), sql_hint_traffic_3( 
    TRAFFIC_ALGORITHM(TYPE(NAME= SQL_HINT))
);

LABELSLOAD_BALANCER 为可选字段,示例中省略字段信息如下。

  • sql_match_traffic 无省略字段

  • sql_hint_traffic_1 省略 LABELS 字段

  • sql_hint_traffic_2 省略 LOAD_BALANCER 字段

  • sql_hint_traffic_3 省略 LABELSLOAD_BALANCER 字段

删除双路由规则:

DROP TRAFFIC RULE sql_hint_traffic_2, sql_hint_traffic_3;

查看规则 #

 -- 查询所有规则
 SHOW TRAFFIC RULES;
 -- 查询指定规则
 SHOW TRAFFIC RULE sql_match_traffic;

示例:查看规则

SHOW TRAFFIC RULE sql_match_traffic;
+--------------------+---------------+----------------+--------------------------------------------------------------------------------+--------------------+---------------------+
| name               | labels        | algorithm_type | algorithm_props                                                                | load_balancer_type | load_balancer_props |
+--------------------+---------------+----------------+--------------------------------------------------------------------------------+--------------------+---------------------+
| sql_match_traffic  | OLTP          | SQL_MATCH      | sql=SELECT * FROM t_order WHERE order_id = ?; UPDATE t_order SET order_id = ?; | RANDOM             | key=value           |
+--------------------+---------------+----------------+--------------------------------------------------------------------------------+--------------------+---------------------+

修改规则 #

ALTER TRAFFIC RULE sql_match_traffic (
    LABELS(OLTP,LIMIT),
    TRAFFIC_ALGORITHM(TYPE(NAME=SQL_MATCH,PROPERTIES("sql" = "SELECT * FROM t_order WHERE order_id = ?;"))),
    LOAD_BALANCER(TYPE(NAME=RANDOM, PROPERTIES("key1"="value1")))
), sql_hint_traffic_1( 
    TRAFFIC_ALGORITHM(TYPE(NAME= SQL_HINT))
), sql_hint_traffic_2( 
    LABELS(OLAP, order_by),
    TRAFFIC_ALGORITHM(TYPE(NAME= SQL_HINT))
), sql_hint_traffic_3( 
    TRAFFIC_ALGORITHM(TYPE(NAME= SQL_HINT)),
    LOAD_BALANCER(TYPE(NAME=ROUND_ROBIN, PROPERTIES("key"="value")))
);

LABELSLOAD_BALANCER 为可选字段,示例中省略字段信息如下。

  • sql_match_traffic 无省略字段
  • sql_hint_traffic_1 省略 LABELS 字段
  • sql_hint_traffic_2 省略 LOAD_BALANCER 字段
  • sql_hint_traffic_3 省略 LABELSLOAD_BALANCER 字段

FAQ #

  1. 同一个事务单元中的语句,可能部分在 JDBC 接入端执行,部分在 Proxy 接入端执行,是否还能够保证事务的一致性?

能够保证事务一致性。事务转发策略的配置中内置了三种事务匹配算法,包含 FIRST_SQL、JDBC、PROXY。

  • FIRST_SQL 会根据第一条 SQL 转发的结果来决定事务单元的转发结果。

  • JDBC 会将事务单元不进行转发,在 JDBC 上执行执行。

  • PROXY 会将事务单元转发到 Proxy 实例执行,为了保证数据的一致性,事务单元会在同一个实例上执行。