Logo
数据库发现

数据库发现 #

高可用是现代系统的最基本诉求,作为系统基石的数据库,对于高可用的要求也是必不可少的。本章节所讨论的高可用,是指 SphereEx-DBPlusEngine 对存储节点可用状态的发现的方案。

在存算分离的分布式数据库体系中,存储节点和计算节点的高可用方案是不同的。 对于有状态的存储节点来说,需要其自身具备数据一致性同步、探活以及主节点选举等能力; 对于无状态的计算节点来说,需要感知存储节点的变化的同时,还需要独立架设负载均衡器,并具备服务发现和请求分发的能力。

概述 #

SphereEx-DBPlusEngine 自身提供计算节点,并通过数据库作为存储节点构建集群。本身并不提供数据库的高可用能力,但可以借助数据库高可用能力,并通过自身数据库发现及动态感知的能力,帮助用户整合主从切换、故障发现、流量切换治理等一体化的数据库高可用方案。因此,它采用的高可用方案是利用数据库自身的高可用方案做存储节点高可用,并自动识别其变化。特别是在分布式场景下搭配主从流量控制的特性,可以提供更为完善的高可用读写分离方案。

SphereEx-DBPlusEngine 需要自动感知多样化的存储节点高可用方案的同时,也能够动态集成对接读写分离方案。

适用场景 #

  • 高可用场景

SphereEx-DBPlusEngine 不提供数据库高可用的能力,它通过感知数据库主从关系的切换来保证系统的可用性。 确切来说,SphereEx-DBPlusEngine 提供数据库发现的能力,自动感知数据库主从关系,并修正计算节点对数据库的连接。

  • 动态读写分离场景

高可用和读写分离一起使用时,读写分离无需配置具体的主库和从库。同时,支持在半同步复制,异步复制时探测从库的延迟时间,并动态路由延迟较低的从库提供读流量。数据库发现能力会动态的识别读写分离的主从关系,并正确地疏导读写流量。高可用还提供从库全部宕机,读流量自动路由至主库,从而保证业务系统的可用性。

数据库支持 #

目前 SphereEx-DBPlusEngine 支持多种数据库架构高可用发现,具体支持类型参考数据库发现算法

使用前提 #

  • 已安装部署 SphereEx-DBPlusEngine 和 Zookeeper,服务运行正常;
  • 已安装部署数据库单主集群,服务运行正常。

不支持项 #

  • 不支持多主集群模式;
  • 不支持级联集群模式;
  • 不支持主主集群模式。

注意事项 #

  • 重复的 ruleName 将无法被创建;
  • 正在被使用的 discoveryTypediscoveryHeartbeat 无法被删除;
  • 带有 - 的命名在改动时需要使用 " "
  • 移除 discoveryRule 时不会移除被该 discoveryRule 使用的 discoveryTypediscoveryHeartbeat

原理介绍 #

数据库发现能力的实现包含了前置检查,动态发现主库,动态发现从库,以及心跳检测机制这四个方面。

数据库发现原理

前置检查 #

在数据库发现配置过程中,SphereEx-DBPlusEngine 会对环境进行检查,当所有检查项均通过校验后,才可正常完成配置。下面以 MGR 集群为例来介绍所检查的内容。

  • 检查是否安装了 MGR 插件,以下命令应输出插件名为 group_replication 的插件信息。
SELECT * FROM information_schema.PLUGINS WHERE PLUGIN_NAME='group_replication';
  • 查看 MGR 组成员数量,MGR 要求最少有 3 个节点,以下命令输出的数字应大于等于 3。
SELECT count(*) FROM performance_schema.replication_group_members;
  • 校验 MGR 集群的 group_replication_group_name 信息是否与配置中的组名一致。
-- group_name 是 MGR 组的标识,一组 MGR 集群对应同一个 group name
SELECT * FROM performance_schema.global_variables WHERE VARIABLE_NAME='group_replication_group_name'; 
  • 查看当前 MGR 是否设置为单主模式,需满足单主模式,group_replication_single_primary_mode 参数值应为 ON。
SELECT * FROM performance_schema.global_variables WHERE VARIABLE_NAME='group_replication_single_primary_mode';
  • 查询 MGR 组集群中所有的节点地址,端口及成员状态信息,用于校验我们配置的数据源是否正确。
SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE FROM performance_schema.replication_group_members;

动态发现主库 #

SphereEx-DBPlusEngine 会通过如下 SQL 命令获取主库 URL:

SELECT MEMBER_HOST, MEMBER_PORT FROM performance_schema.replication_group_members WHERE MEMBER_ID = "
SELECT VARIABLE_VALUE FROM performance_schema.global_status WHERE VARIABLE_NAME = 'group_replication_primary_member'

然后将输出的结果与我们所配置的数据源的 URL 逐一去对比,命中的数据源则为主库,将主库更新至注册中心,通过注册中心分发至集群内其它计算节点中。

动态发现从库 #

SphereEx-DBPlusEngine 会将从库状态分为启动与禁用,并且从库的状态实时地同步至 SphereEx-DBPlusEngine 中,以保证读流量可以被正确的路由。

  • 获取 MGR 组中所有的节点
SELECT MEMBER_HOST, MEMBER_PORT, MEMBER_STATE FROM performance_schema.replication_group_members;
  • 禁用从库

从库的禁用,依据的是我们配置的数据源以及 MGR 组中所有的节点。SphereEx-DBPlusEngine 会逐一检查我们配置的数据源是否可以正常的获取 Connection,并校验数据源 URL 是否包含 MGR 组中的节点。无法正常获取 Connection 或校验失败,SphereEx-DBPlusEngine 会事件驱动禁用数据源,以及同步注册中心。

  • 启用从库

恢复宕机从库,重新加入 MGR 组后,检查我们配置中是否使用了被恢复的数据源。如果使用,则会告诉 SphereEx-DBPlusEngine 需要该数据源恢复成启用状态。

心跳检测机制 #

为了保证主从状态同步的实时性,高可用模块引入心跳检测机制,在高可用模块初始化时将上述的流程以 Job 的方式交由调度框架执行,实现了功能开发和作业调度的分离。我们可以根据业务特点,灵活配置心跳周期。

操作指南 #

  1. 准备环境,完成 SphereEx-DBPlusEngine 集群及数据库集群部署;
  2. 创建逻辑库,注册存储(数据库)资源;
  3. 创建数据库发现规则,包括存储节点信息,数据库发现类型及心跳机制;
  4. 创建动态读写分离规则;
  5. 确认数据库发现规则配置信息,完成配置。

配置示例 #

以 3 节点 MGR 场景为例,基于动态读写分离的场景,数据库发现规则配置如下所示。

拓扑图

  1. 在 MGR 主节点中创建名为 ha 的数据库
mysql -utest -h192.168.56.104 -P3306 -p

SELECT * FROM performance_schema.replication_group_members;

-- 连接主库 
DROP SCHEMA IF EXISTS ha;
CREATE SCHEMA IF NOT EXISTS ha;
  1. 在 Proxy 中创建名为 testdb 的逻辑库
mysql -uroot -p -P3307 -h127.0.0.1

mysql> CREATE DATABASE testdb;
Query OK, 0 rows affected (0.05 sec)

mysql> USE testdb;
Database changed
  1. 将 MGR 各个节点注册到 Proxy 中
mysql> REGISTER STORAGE UNIT ds_0 (
    URL="jdbc:mysql://192.168.xx.103:3306/ha?serverTimezone=UTC&useSSL=false",
    USER="test",
    PASSWORD="Test@123"
), ds_1 (
    URL="jdbc:mysql://192.168.xx.104:3306/ha?serverTimezone=UTC&useSSL=false",
    USER="test",
    PASSWORD="Test@123"
), ds_2 (
    URL="jdbc:mysql://192.168.xx.105:3306/ha?serverTimezone=UTC&useSSL=false",
    USER="test",
    PASSWORD="Test@123"
); 

查看当前已注册的数据源。

mysql> SHOW STORAGE UNITS;
+------+-------+----------------+------+------+---------------------------------+---------------------------+---------------------------+---------------+---------------+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| name | type  | host           | port | db   | connection_timeout_milliseconds | idle_timeout_milliseconds | max_lifetime_milliseconds | max_pool_size | min_pool_size | read_only | other_attributes                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
+------+-------+----------------+------+------+---------------------------------+---------------------------+---------------------------+---------------+---------------+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| ds_2 | MySQL | 192.168.xx.105 | 3306 | ha   | 30000                           | 60000                     | 2100000                   | 50            | 1             | false     | {"dataSourceProperties":{"cacheServerConfiguration":"true","elideSetAutoCommits":"true","useServerPrepStmts":"true","cachePrepStmts":"true","rewriteBatchedStatements":"true","cacheResultSetMetadata":"false","useLocalSessionState":"true","maintainTimeStats":"false","prepStmtCacheSize":"8192","tinyInt1isBit":"false","prepStmtCacheSqlLimit":"2048","netTimeoutForStreamingResults":"0","zeroDateTimeBehavior":"round"},"healthCheckProperties":{},"initializationFailTimeout":1,"validationTimeout":5000,"keepaliveTime":0,"leakDetectionThreshold":0,"registerMbeans":false,"allowPoolSuspension":false,"autoCommit":true,"isolateInternalQueries":false} |
| ds_1 | MySQL | 192.168.xx.104 | 3306 | ha   | 30000                           | 60000                     | 2100000                   | 50            | 1             | false     | {"dataSourceProperties":{"cacheServerConfiguration":"true","elideSetAutoCommits":"true","useServerPrepStmts":"true","cachePrepStmts":"true","rewriteBatchedStatements":"true","cacheResultSetMetadata":"false","useLocalSessionState":"true","maintainTimeStats":"false","prepStmtCacheSize":"8192","tinyInt1isBit":"false","prepStmtCacheSqlLimit":"2048","netTimeoutForStreamingResults":"0","zeroDateTimeBehavior":"round"},"healthCheckProperties":{},"initializationFailTimeout":1,"validationTimeout":5000,"keepaliveTime":0,"leakDetectionThreshold":0,"registerMbeans":false,"allowPoolSuspension":false,"autoCommit":true,"isolateInternalQueries":false} |
| ds_0 | MySQL | 192.168.xx.103 | 3306 | ha   | 30000                           | 60000                     | 2100000                   | 50            | 1             | false     | {"dataSourceProperties":{"cacheServerConfiguration":"true","elideSetAutoCommits":"true","useServerPrepStmts":"true","cachePrepStmts":"true","rewriteBatchedStatements":"true","cacheResultSetMetadata":"false","useLocalSessionState":"true","maintainTimeStats":"false","prepStmtCacheSize":"8192","tinyInt1isBit":"false","prepStmtCacheSqlLimit":"2048","netTimeoutForStreamingResults":"0","zeroDateTimeBehavior":"round"},"healthCheckProperties":{},"initializationFailTimeout":1,"validationTimeout":5000,"keepaliveTime":0,"leakDetectionThreshold":0,"registerMbeans":false,"allowPoolSuspension":false,"autoCommit":true,"isolateInternalQueries":false} |
+------+-------+----------------+------+------+---------------------------------+---------------------------+---------------------------+---------------+---------------+-----------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
3 rows in set (0.00 sec)
  1. 创建数据库发现规则

在创建数据库发现规则时,我们需要配置数据源,数据库发现类型,及心跳。对于 MGR 的类型,还需要填入 group-name 信息,具体值可通过 my.cnf 来确认。 结合业务需要,设置数据库发现的心跳周期,此处配置方式同 crontab 格式相同,最小单位为秒。以下示例中为每 5 秒探测一次数据库节点情况。

mysql> CREATE DB_DISCOVERY RULE primary_replica_ds (
    STORAGE_UNITS(ds_0, ds_1, ds_2),
    TYPE(NAME='SphereEx:MySQL.MGR',PROPERTIES('group-name'='b309fcc3-93e8-11ec-b5bd-080027c850b1')),
    HEARTBEAT(PROPERTIES('keep-alive-cron'='0/5 * * * * ?'))
);
Query OK, 0 rows affected (0.28 sec)

数据库发现规则创建完成后,通过 SHOW 命令进行确认。

mysql> SHOW DB_DISCOVERY RULES;
+--------------------+-------------------+--------------------------+--------------------------------------------------------------------------------------------------------------+-------------------------------------+
| group_name         | data_source_names | primary_data_source_name | discovery_type                                                                                               | discovery_heartbeat                 |
+--------------------+-------------------+--------------------------+--------------------------------------------------------------------------------------------------------------+-------------------------------------+
| primary_replica_ds | ds_0,ds_1,ds_2    | ds_0                     | {name=primary_replica_ds_mysql_mgr, type=SphereEx:MySQL.MGR, props={group-name=b309fcc3-93e8-11ec-b5bd-080027c850b1}} | {name=primary_replica_ds_heartbeat} |
+--------------------+-------------------+--------------------------+--------------------------------------------------------------------------------------------------------------+-------------------------------------+
1 row in set (0.06 sec)

mysql> SHOW DB_DISCOVERY HEARTBEATS;
+------------------------------+---------------------------------+
| name                         | props                           |
+------------------------------+---------------------------------+
| primary_replica_ds_heartbeat | {keep-alive-cron=0/5 * * * * ?} |
+------------------------------+---------------------------------+
1 row in set (0.01 sec)

如上输出展示了具体数据库发现规则的配置,规则已配置完成。

  1. 创建动态读写分离规则

对于有动态读写分离需求的场景,可在该步骤进行读写分离规则的创建。在动态读写分离配置中写入自定义的数据库发现规则的名称即可,根据上一步骤内容所示,此处应填入 primary_replica_ds

mysql> CREATE READWRITE_SPLITTING RULE dynamic_readwrite_ds (
    AUTO_AWARE_RESOURCE=primary_replica_ds
);
Query OK, 0 rows affected (0.08 sec)

接下来,确认读写分离规则。

mysql> SHOW READWRITE_SPLITTING RULES;
+----------------------+-----------------------------+---------------------------------+-------------------------+-------------------------+--------------------+---------------------+
| name                 | auto_aware_data_source_name | write_data_source_query_enabled | write_storage_unit_name | read_storage_unit_names | load_balancer_type | load_balancer_props |
+----------------------+-----------------------------+---------------------------------+-------------------------+-------------------------+--------------------+---------------------+
| dynamic_readwrite_ds | primary_replica_ds          | NULL                            | ds_0                    | ds_1,ds_2               |                    |                     |
+----------------------+-----------------------------+---------------------------------+-------------------------+-------------------------+--------------------+---------------------+
1 row in set (0.04 sec)

通过输出内容可确认,当前 ds_0 为写节点,ds_1 和 ds_2 为读节点。数据库发现规则和动态读写分离规则配置完毕。