双路由 #
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 是否需要转发的算法,包括了 HintTrafficAlgorithm
、SegmentTrafficAlgorithm
和 TransactionTrafficAlgorithm
。
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 实例上执行,目前支持 RANDOM
、ROUND_ROBIN
和 WEIGHT
三种负载均衡算法。
算法类型的详情,请参见内置算法 - 负载均衡算法。
适用场景 #
混合部署 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 内部在初始化路由策略时,会根据是否针对事务场景,将策略进行分类维护,针对事务场景的策略,只有在开启事务时使用,否则使用其他非事务策略。
使用指南 #
- 确认当前系统中消耗资源较高的 SQL,如聚合类语句,提取具体语句信息。可通过 SphereEx-Console 或其他监控工具来捕获。
- 在 DBPlusEngine-Proxy 中,通过 DistSQL 完成指定 SQL 的配置。
- 在 DBPlusEngine-Proxy 中开启 SQL 显示功能,业务发起新的请求,确认指定 SQL 的路由情况。
操作指南 #
- 确认需要转发到 Proxy 运行的 SQL 语句;
- 在 Proxy 中,通过 DistSQL 完成双路由规则的配置;
- 在 Proxy 中开启 SQL 展示;
- 应用测发起请求,通过 Proxy 日志确认 SQL;
- 确认结果无误后,在 Proxy 中关闭 SQL 展示;
配置示例 #
环境说明 #
实例 | IP 地址 | 服务端口 | 主机名 | 备注 | |
---|---|---|---|---|---|
1 | DBPlusEngine 1.2.0 | 192.168.xx.102 | 3307 | dbplusengine | |
2 | Zookeeper 3.6.3 | 192.168.xx.102 | 2181 | dbplusengine | |
3 | MySQL 8.0.28 | 192.168.xx.103 | 3306 | mysql_0 | |
4 | MySQL 8.0.28 | 192.168.xx.104 | 3306 | mysql_1 |
拓扑图 #
配置过程 #
确认需要转发到 Proxy 运行的 SQL 语句
配置双路由规则
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;
- 在 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)
应用测发起请求,通过 Proxy 日志确认 SQL
在 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))
);
LABELS
与 LOAD_BALANCER
为可选字段,示例中省略字段信息如下。
sql_match_traffic 无省略字段
sql_hint_traffic_1 省略
LABELS
字段sql_hint_traffic_2 省略
LOAD_BALANCER
字段sql_hint_traffic_3 省略
LABELS
与LOAD_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")))
);
LABELS
与 LOAD_BALANCER
为可选字段,示例中省略字段信息如下。
- sql_match_traffic 无省略字段
- sql_hint_traffic_1 省略
LABELS
字段 - sql_hint_traffic_2 省略
LOAD_BALANCER
字段 - sql_hint_traffic_3 省略
LABELS
与LOAD_BALANCER
字段
FAQ #
- 同一个事务单元中的语句,可能部分在 JDBC 接入端执行,部分在 Proxy 接入端执行,是否还能够保证事务的一致性?
能够保证事务一致性。事务转发策略的配置中内置了三种事务匹配算法,包含 FIRST_SQL、JDBC、PROXY。
FIRST_SQL 会根据第一条 SQL 转发的结果来决定事务单元的转发结果。
JDBC 会将事务单元不进行转发,在 JDBC 上执行执行。
PROXY 会将事务单元转发到 Proxy 实例执行,为了保证数据的一致性,事务单元会在同一个实例上执行。