使用 Apache ShardingSphere 创建高可用分布式数据库

按照 ShardingSphere 高可用性和动态读/写分离的这个示例,作为您自己配置的基础。 
目前还没有读者喜欢这个。
Databases as a service

Jason Baker。CC BY-SA 4.0。

在数字时代,现代商业系统必须是高可用、可靠和稳定的。作为当前业务系统的基石,数据库应该拥抱高可用性。

高可用性 (HA) 允许数据库在主数据库节点和备数据库节点之间切换服务。HA 自动选择主节点,在前一个节点崩溃时选择最佳节点。

MySQL 高可用性

有很多 MySQL 高可用性选项,每个选项都有优点和缺点。以下是几个常见的高可用性选项

  • Orchestrator 是一个用 Go 语言编写的 MySQL HA 和复制拓扑管理工具。它的优势在于支持手动调整主备拓扑,自动故障转移,以及通过图形化 Web 控制台自动或手动恢复主节点。但是,该程序需要单独部署,并且由于其复杂的配置而具有陡峭的学习曲线。
  • MHA 是另一个成熟的解决方案。它提供主/备切换和故障转移功能。它的优点是可以在切换过程中确保最少的数据丢失,并且可以与半同步和异步复制框架一起工作。但是,MHA 启动后只监控主节点,并且 MHA 不为读取数据库提供负载均衡功能。
  • MGR 基于分布式 Paxos 协议实现组复制,以确保数据一致性。它是 MySQL 提供的官方 HA 组件,不需要额外的部署程序。相反,用户只需要在每个数据源节点上安装 MGR 插件。该工具具有高一致性、容错性、可扩展性和灵活性等特点。

Apache ShardingSphere 高可用性

Apache ShardingSphere 的架构实际上将存储与计算分离。存储节点代表底层数据库,例如 MySQL、PostgreSQL、openGauss 等,而计算节点指的是 ShardingSphere-JDBCShardingSphere-Proxy

因此,存储节点和计算节点的 高可用性解决方案 是不同的。无状态计算节点需要感知存储节点的变化。它们还需要设置单独的负载均衡器,并具有服务发现和请求分发的能力。有状态存储节点必须提供数据同步、连接测试、主节点选举等功能。

虽然 ShardingSphere 不提供具有高可用性的数据库,但它可以借助数据库 HA 及其数据库发现和动态感知能力,帮助用户集成数据库 HA 解决方案,例如主备切换、故障发现、流量切换治理等。

当与分布式场景中的主备流量控制功能结合使用时,ShardingSphere 可以提供更好的高可用性读/写分离解决方案。使用 DistSQL 的动态高可用性调整规则来获取主/备节点的信息,将更容易操作和管理 ShardingSphere 集群。

最佳实践

Apache ShardingSphere 采用面向插件的架构,因此您可以独立或一起使用其所有增强的功能。其高可用性功能通常与读/写分离一起使用,根据负载均衡算法将查询请求分发到备数据库,以确保系统 HA,减轻主数据库压力,并提高业务系统吞吐量。

请注意,ShardingSphere HA 的实现依赖于其分布式治理能力。因此,目前只能在集群模式下使用。同时,读/写分离规则在 ShardingSphere 5.1.0 中进行了修订。有关详细信息,请参阅关于 读/写分离 的官方文档。

以下示例考虑了使用 ShardingSphere DistSQL RAL 语句的 HA+读/写分离配置。该示例从配置、需求和初始 SQL 开始。

配置

schemaName: database_discovery_db

dataSources:
  ds_0:
    url: jdbc:mysql://127.0.0.1:1231/demo_primary_ds?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 3000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_1:
    url: jdbc:mysql://127.0.0.1:1232/demo_primary_ds?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 3000
    idleTimeoutMilliseconds: 60000
    maxLifetimeMilliseconds: 1800000
    maxPoolSize: 50
    minPoolSize: 1
  ds_2:
    url: jdbc:mysql://127.0.0.1:1233/demo_primary_ds?serverTimezone=UTC&useSSL=false
    username: root
    password: 123456
    connectionTimeoutMilliseconds: 3000
    idleTimeoutMilliseconds: 50000
    maxLifetimeMilliseconds: 1300000
    maxPoolSize: 50
    minPoolSize: 1

rules:
  - !READWRITE_SPLITTING
    dataSources:
      replication_ds:
        type: Dynamic
        props:
          auto-aware-data-source-name: mgr_replication_ds
  - !DB_DISCOVERY
    dataSources:
      mgr_replication_ds:
        dataSourceNames:
          - ds_0
          - ds_1
          - ds_2
        discoveryHeartbeatName: mgr-heartbeat
        discoveryTypeName: mgr
    discoveryHeartbeats:
      mgr-heartbeat:
        props:
          keep-alive-cron: '0/5 * * * * ?'
    discoveryTypes:
      mgr:
        type: MGR
        props:
          group-name: b13df29e-90b6-11e8-8d1b-525400fc3996

需求

  • ShardingSphere-Proxy 5.1.0(集群模式 + HA + 动态读/写分离规则)
  • Zookeeper 3.7.0
  • MySQL MGR 集群

SQL 脚本

CREATE TABLE `t_user` (
  `id` int(8) NOT NULL,
  `mobile` char(20) NOT NULL,
  `idcard` varchar(18) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

首先,查看主备关系

mysql> SHOW READWRITE_SPLITTING RULES;
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
| name           | auto_aware_data_source_name | write_data_source_name | read_data_source_names | load_balancer_type | load_balancer_props |
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
| replication_ds | mgr_replication_ds          | ds_0                   | ds_1,ds_2              | NULL               |                     |
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
1 row in set (0.09 sec)

您还可以查看备数据库状态

mysql> SHOW READWRITE_SPLITTING READ RESOURCES;
+----------+---------+
| resource | status  |
+----------+---------+
| ds_1     | enabled |
| ds_2     | enabled |
+----------+---------+

以上结果显示,主数据库当前为 ds_0,而备数据库为 ds_1ds_2

接下来,测试 INSERT

mysql> INSERT INTO t_user(id, mobile, idcard) value (10000, '13718687777', '141121xxxxx');
Query OK, 1 row affected (0.10 sec)

查看 ShardingSphere-Proxy 日志,看看路由节点是否为主数据库 ds_0。

[INFO ] 2022-02-28 15:28:21.495 [ShardingSphere-Command-2] ShardingSphere-SQL - Logic SQL: INSERT INTO t_user(id, mobile, idcard) value (10000, '13718687777', '141121xxxxx')
[INFO ] 2022-02-28 15:28:21.495 [ShardingSphere-Command-2] ShardingSphere-SQL - SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
[INFO ] 2022-02-28 15:28:21.495 [ShardingSphere-Command-2] ShardingSphere-SQL - Actual SQL: ds_0 ::: INSERT INTO t_user(id, mobile, idcard) value (10000, '13718687777', '141121xxxxx')

现在测试 SELECT(重复两次)

mysql> SELECT id, mobile, idcard FROM t_user WHERE id = 10000;

查看 ShardingSphere-Proxy 日志,看看路由节点是否为 ds_1ds_2

[INFO ] 2022-02-28 15:34:07.912 [ShardingSphere-Command-4] ShardingSphere-SQL - Logic SQL: SELECT id, mobile, idcard FROM t_user WHERE id = 10000
[INFO ] 2022-02-28 15:34:07.913 [ShardingSphere-Command-4] ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
[INFO ] 2022-02-28 15:34:07.913 [ShardingSphere-Command-4] ShardingSphere-SQL - Actual SQL: ds_1 ::: SELECT id, mobile, idcard FROM t_user WHERE id = 10000
[INFO ] 2022-02-28 15:34:21.501 [ShardingSphere-Command-4] ShardingSphere-SQL - Logic SQL: SELECT id, mobile, idcard FROM t_user WHERE id = 10000
[INFO ] 2022-02-28 15:34:21.502 [ShardingSphere-Command-4] ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
[INFO ] 2022-02-28 15:34:21.502 [ShardingSphere-Command-4] ShardingSphere-SQL - Actual SQL: ds_2 ::: SELECT id, mobile, idcard FROM t_user WHERE id = 10000

切换到主数据库

关闭主数据库 ds_0

Close primary database

(赵锦超,CC BY-SA 4.0)

通过 DistSQL 查看主数据库是否已更改,以及备数据库状态是否正确

[INFO ] 2022-02-28 15:34:07.912 [ShardingSphere-Command-4] ShardingSphere-SQL - Logic SQL: SELECT id, mobile, idcard FROM t_user WHERE id = 10000
[INFO ] 2022-02-28 15:34:07.913 [ShardingSphere-Command-4] ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
[INFO ] 2022-02-28 15:34:07.913 [ShardingSphere-Command-4] ShardingSphere-SQL - Actual SQL: ds_1 ::: SELECT id, mobile, idcard FROM t_user WHERE id = 10000
[INFO ] 2022-02-28 15:34:21.501 [ShardingSphere-Command-4] ShardingSphere-SQL - Logic SQL: SELECT id, mobile, idcard FROM t_user WHERE id = 10000
[INFO ] 2022-02-28 15:34:21.502 [ShardingSphere-Command-4] ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
[INFO ] 2022-02-28 15:34:21.502 [ShardingSphere-Command-4] ShardingSphere-SQL - Actual SQL: ds_2 ::: SELECT id, mobile, idcard FROM t_user WHERE id = 10000

现在,INSERT 另一行数据

mysql> INSERT INTO t_user(id, mobile, idcard) value (10001, '13521207777', '110xxxxx');
Query OK, 1 row affected (0.04 sec)

查看 ShardingSphere-Proxy 日志,看看路由节点是否为主数据库 ds_1

[INFO ] 2022-02-28 15:40:26.784 [ShardingSphere-Command-6] ShardingSphere-SQL - Logic SQL: INSERT INTO t_user(id, mobile, idcard) value (10001, '13521207777', '110xxxxx')
[INFO ] 2022-02-28 15:40:26.784 [ShardingSphere-Command-6] ShardingSphere-SQL - SQLStatement: MySQLInsertStatement(setAssignment=Optional.empty, onDuplicateKeyColumns=Optional.empty)
[INFO ] 2022-02-28 15:40:26.784 [ShardingSphere-Command-6] ShardingSphere-SQL - Actual SQL: ds_1 ::: INSERT INTO t_user(id, mobile, idcard) value (10001, '13521207777', '110xxxxx')

最后,测试 SELECT(重复两次)

mysql> SELECT id, mobile, idcard FROM t_user WHERE id = 10001;

查看 ShardingSphere-Proxy 日志,看看路由节点是否为 ds_2

[INFO ] 2022-02-28 15:42:00.651 [ShardingSphere-Command-7] ShardingSphere-SQL - Logic SQL: SELECT id, mobile, idcard FROM t_user WHERE id = 10001
[INFO ] 2022-02-28 15:42:00.651 [ShardingSphere-Command-7] ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
[INFO ] 2022-02-28 15:42:00.651 [ShardingSphere-Command-7] ShardingSphere-SQL - Actual SQL: ds_2 ::: SELECT id, mobile, idcard FROM t_user WHERE id = 10001
[INFO ] 2022-02-28 15:42:02.148 [ShardingSphere-Command-7] ShardingSphere-SQL - Logic SQL: SELECT id, mobile, idcard FROM t_user WHERE id = 10001
[INFO ] 2022-02-28 15:42:02.149 [ShardingSphere-Command-7] ShardingSphere-SQL - SQLStatement: MySQLSelectStatement(table=Optional.empty, limit=Optional.empty, lock=Optional.empty, window=Optional.empty)
[INFO ] 2022-02-28 15:42:02.149 [ShardingSphere-Command-7] ShardingSphere-SQL - Actual SQL: ds_2 ::: SELECT id, mobile, idcard FROM t_user WHERE id = 10001

释放备数据库

Release the secondary database

(赵锦超,CC BY-SA 4.0)

通过 DistSQL 查看最新的主备关系变化。 ds_0 节点的状态已恢复为启用状态,而 ds_0 已集成到 read_data_source_names

mysql> SHOW READWRITE_SPLITTING RULES;
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
| name           | auto_aware_data_source_name | write_data_source_name | read_data_source_names | load_balancer_type | load_balancer_props |
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
| replication_ds | mgr_replication_ds          | ds_1                   | ds_0,ds_2              | NULL               |                     |
+----------------+-----------------------------+------------------------+------------------------+--------------------+---------------------+
1 row in set (0.01 sec)

mysql> SHOW READWRITE_SPLITTING READ RESOURCES;
+----------+---------+
| resource | status  |
+----------+---------+
| ds_0     | enabled |
| ds_2     | enabled |
+----------+---------+
2 rows in set (0.00 sec)

总结

数据库高可用性在当今的商业环境中至关重要,而 Apache ShardingSphere 可以帮助提供必要的可靠性。基于以上示例,您现在更了解 ShardingSphere 的高可用性和动态读/写分离。使用此示例作为您自己配置的基础。 

标签

评论已关闭。

知识共享许可协议本作品根据知识共享署名-相同方式共享 4.0 国际许可协议获得许可。
© . All rights reserved.