赵锦超
SphereEx 中间件研发工程师,Apache ShardingSphere Committer。目前专注于 ShardingSphere 高可用的设计和研发。
什么是数据库高可用
在这个以“数据”为王的时代,对现代业务系统可用性、可靠性以及稳定性要求极其高。数据库又作为现代业务系统的基石,因此数据库高可用就显得尤为重要。同时数据库高可用应具备主从切换以及自动选主的能力,当我们主库宕机时,可以快速地在从节点中选举最佳节点来作为主库。
MySQL 高可用介绍
MySQL 高可用方案比较多,且每个高可用方案都有各自的优缺点。在这里我们列举几个常用的高可用方案。
Orchestrator 是一款 Go 语言编写的 MySQL 高可用性和复制拓扑管理工具,优点是支持通过 Web 可视化控制台手动的调整主从拓扑结构、自动故障转移、自动或手动恢复主节点。缺点则是需要独立部署该程序,学习成本较高配置复杂;
MHA 也是一款相对成熟的 MySQL 高可用软件。支持故障切换和主从提升,其优点是在切换的过程中可以最大程度保证数据不丢失,同时可以工作在半同步复制或异步复制集群下。缺点则是 MHA 启动后只对主库进行监控,并且读库没有提供负载均衡功能;
MGR 是基于分布式 Paxos 协议实现组复制,从而保证数据的一致性。同时也是 MySQL 官方提供解决 MySQL 高可用的组件,使用它我们不需要单独的部署程序,只需要在每个数据源节点中安装 MGR Plugin,其特点具有高一致性、高容错性、高扩展性、高灵活性。
Apache ShardingSphere 高可用
Apache ShardingSphere 采用存算分离架构体系,存储节点代表底层数据库,如 MySQL、PostgreSQL、openGauss 等,计算节点则是指 ShardingSphere-JDBC 或 ShardingSphere-Proxy,并且存储节点和计算节点的高可用方案是不同的。对于无状态的计算节点来说,需要感知存储节点变化的同时,还需要独立架设负载均衡器,并具备服务发现和请求分发的能力。对于有状态的存储节点来说,需要其自身具备数据一致性同步、探活、主节点选举等能力。
ShardingSphere 虽然本身并不提供数据库的高可用能力,但可以借助数据库高可用能力,并通过自身数据库发现及动态感知的能力,帮助用户整合主从切换、故障发现、流量切换治理等一体化的数据库高可用方案。特别是在分布式场景下搭配主从流量控制的特性,可以提供更为完善的高可用读写分离方案。同时我们在使用高可用功能时,再搭配使用 DistSQL 动态调整高可用规则的动态,获取主从的节点信息等,使我们更能方便地运维管控 ShardingSphere 集群。
最佳实践
Apache ShardingSphere 采用可插拔架构,所有增量的功能能单独使用也能相互组合。高可用功能通常和读写分离配合使用,在保证系统高可用的同时,配合读写分离将查询请求根据负载均衡算法分散到从库中,减少主库的压力,提升业务系统的吞吐量。以下实践内容将采用高可用 + 读写分离的配置,再配合使用 ShardingSphere DistSQL RAL(https://shardingsphere.apache.org/document/current/cn/user-manual/shardingsphere-proxy/distsql/syntax/ral/)语句进行演示。
需要注意的是,ShardingSphere 高可用的实现依赖于分布式治理的能力,所以目前只支持在集群模式下使用。同时读写分离规则在 ShardingSphere 5.1.0 版本中也进行了调整,详细内容请参考官方文档读写分离。(https://shardingsphere.apache.org/document/current/cn/user-manual/shardingsphere-jdbc/yaml-config/rules/readwrite-splitting/)
配置参考
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 (Cluster mode + 高可用+动态读写分离规则)
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=