在数字时代,现代商业系统必须是高可用、可靠和稳定的。作为当前业务系统的基石,数据库应该拥抱高可用性。
高可用性 (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-JDBC 或 ShardingSphere-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_1 和 ds_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_1 或 ds_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

(赵锦超,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
释放备数据库

(赵锦超,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 的高可用性和动态读/写分离。使用此示例作为您自己配置的基础。
评论已关闭。