MySQL 8.0 官方推出过一个高可用方案 ReplicaSet 主要由 Router、MySQL Shell、MySQL Server 三个组件组成。
MySQL Shell 负责管理 ReplicaSet 包括部署、切换、节点加入等,都可以通过内置 AdminAPI 自动化完成。
MySQL Router 是一款轻量级中间件,可在应用程序和 ReplicaSet 之间提供透明路由和读写分离功能。
8.0 时期的 Router 读写需要配置两个端口,应用如果想要使用读写分离的特性,需要额外适配。
-- MySQL 经典协议
- Read/Write Connections: localhost:6446, /data/myrouter/mysql.sock
- Read/Only Connections: localhost:6447, /data/myrouter/mysqlro.sock
-- MySQL X 协议
- Read/Write Connections: localhost:6448, /data/myrouter/mysqlx.sock
- Read/Only Connections: localhost:6449, /data/myrouter/mysqlxro.sock
到了 MySQL 8.2 版本 Router 自动可以识别读取和写入,按照配置规则分配到不同的实例,无需使用额外端口。
让我们一起从 0 部署一套 MySQL 8.2 ReplicaSet 集群吧 !
IP | hostname | Role |
---|---|---|
172.16.104.56 | 172-16-104-56 | Master |
172.16.104.57 | 172-16-104-56 | Secondary |
vi /etc/hosts
172.16.104.56 172-16-104-56
172.16.104.57 172-16-104-57
MySQL 官网下载地址:https://downloads.mysql.com/archives/community
压缩包里面包含 MySQL Server 和 MySQL Router 组件。安装 MySQL Server 步骤参考下文
MySQL 部署:MySQL 自动化安装部署
官方下载地址:MySQL Shell download
mv mysql-shell-8.0.31-linux-glibc2.12-x86-64bit /usr/local/mysqlsh
export PATH=/usr/local/mysqlsh/bin/:$PATH
echo 'PATH=$PATH:/usr/local/mysqlsh/bin/' >> /etc/profile
进入 MySQL Shell 使用 \py & \js 可以切换命令模式,本篇文章使用 python 模式演示:
部署好 MySQL 实例后,需要创建一个用户,提供给 MySQL Shell 使用,这里演示创建的用户是:rw_shell
GRANT CLONE_ADMIN, CONNECTION_ADMIN, CREATE USER, EXECUTE, FILE, GROUP_REPLICATION_ADMIN, PERSIST_RO_VARIABLES_ADMIN, PROCESS, RELOAD, REPLICATION CLIENT, REPLICATION SLAVE, REPLICATION_APPLIER, REPLICATION_SLAVE_ADMIN, ROLE_ADMIN, SELECT, SHUTDOWN, SYSTEM_VARIABLES_ADMIN ON *.* TO 'rw_shell'@'%' WITH GRANT OPTION;
GRANT DELETE, INSERT, UPDATE ON mysql.* TO 'rw_shell'@'%' WITH GRANT OPTION;
GRANT ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE VIEW, DELETE, DROP, EVENT, EXECUTE, INDEX, INSERT, LOCK TABLES, REFERENCES, SHOW VIEW, TRIGGER, UPDATE ON mysql_innodb_cluster_metadata.* TO 'rw_shell'@'%' WITH GRANT OPTION;
GRANT ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE VIEW, DELETE, DROP, EVENT, EXECUTE, INDEX, INSERT, LOCK TABLES, REFERENCES, SHOW VIEW, TRIGGER, UPDATE ON mysql_innodb_cluster_metadata_bkp.* TO 'rw_shell'@'%' WITH GRANT OPTION;
GRANT ALTER, ALTER ROUTINE, CREATE, CREATE ROUTINE, CREATE TEMPORARY TABLES, CREATE VIEW, DELETE, DROP, EVENT, EXECUTE, INDEX, INSERT, LOCK TABLES, REFERENCES, SHOW VIEW, TRIGGER, UPDATE ON mysql_innodb_cluster_metadata_previous.* TO 'rw_shell'@'%' WITH GRANT OPTION;
配置 MySQL 实例,检查是否符合创建 ReplicaSet 的条件,如果发现需要修改的地方,就会提示确认修改。
执行次命令会让用户输入密码,保存后会进入检查阶段。
dba.configure_replica_set_instance('rw_shell@172-16-104-55:3306', {"clusterAdmin": "rw_shell@'%'"})
dba.configure_replica_set_instance('rw_shell@172-16-104-56:3306', {"clusterAdmin": "rw_shell@'%'"})
MySQL Shell 会检查数据库参数是否满足创建 ReplicaSet 条件,并提出修改建议:
此阶段执行完成后,表示实例都具备组成 ReplicaSet 条件,可进入下一步。
使用 MySQL Shell 连接到主实例,否则测试中会报 session 错误。
An open session is required to perform this operation.
\c rw_shell@172-16-104-56:3306
创建 ReplicaSet 默认当前进入的实例为主库实例:
rs = dba.create_replica_set("prd_op_service")
添加复制节点:
rs.add_instance("172-16-104-57:3306")
至此,ReplicaSet 已配置完成,下图为拓扑信息:
{
"replicaSet": {
"name": "prd_op_service",
"primary": "172-16-104-56:3306",
"status": "AVAILABLE",
"statusText": "All instances available.",
"topology": {
"172-16-104-56:3306": {
"address": "172-16-104-56:3306",
"instanceRole": "PRIMARY",
"mode": "R/W",
"status": "ONLINE"
},
"172-16-104-57:3306": {
"address": "172-16-104-57:3306",
"instanceRole": "SECONDARY",
"mode": "R/O",
"replication": {
"applierStatus": "APPLIED_ALL",
"applierThreadState": "Waiting for an event from Coordinator",
"applierWorkerThreads": 4,
"receiverStatus": "ON",
"receiverThreadState": "Waiting for source to send event",
"replicationLag": null,
"replicationSsl": "TLS_AES_128_GCM_SHA256 TLSv1.3",
"replicationSslMode": "REQUIRED"
},
"status": "ONLINE"
}
},
"type": "ASYNC"
}
}
解压二进制包,开箱即用。
xz -d mysql-router-8.2.0-linux-glibc2.12-x86_64.tar.xz
tar -xvf mysql-router-8.2.0-linux-glibc2.12-x86_64.tar
创建 router 专用用户:
rs.setup_router_account('op_router')
更新 router 配置信息:
./mysqlrouter \
--bootstrap rw_shell@172-16-104-56:3306 \
--directory /data/myrouter \
--conf-use-sockets \
--account op_router \
--user=root
此时 Router 输出的配置信息:
After this MySQL Router has been started with the generated configuration
$ ./mysqlrouter -c /data/myrouter/mysqlrouter.conf
InnoDB ReplicaSet 'prd_op_service' can be reached by connecting to:
## MySQL Classic protocol
- Read/Write Connections: localhost:6446, /data/myrouter/mysql.sock
- Read/Only Connections: localhost:6447, /data/myrouter/mysqlro.sock
- Read/Write Split Connections: localhost:6450, /data/myrouter/mysqlsplit.sock
## MySQL X protocol
- Read/Write Connections: localhost:6448, /data/myrouter/mysqlx.sock
- Read/Only Connections: localhost:6449, /data/myrouter/mysqlxro.sock
从输出结果看,相比于之前的版本,多了一个 6450 端口。
启动 mysqlrouter 服务:
./mysqlrouter -c /data/myrouter/mysqlrouter.conf &
使用 6450 端口连接 MySQL:
mysql -urw_shell -P6450 -h172.16.104.56 -p
rw_shell@mysql 17:21: [(none)]>select @@hostname;
+---------------+
| @@hostname |
+---------------+
| 172-16-104-57 |
+---------------+
1 row in set (0.01 sec)
rw_shell@mysql 17:22: [(none)]>begin;
Query OK, 0 rows affected (0.01 sec)
rw_shell@mysql 17:22: [(none)]>select @@hostname;
+---------------+
| @@hostname |
+---------------+
| 172-16-104-56 |
+---------------+
1 row in set (0.01 sec)
rw_shell@mysql 17:22: [(none)]>commit;
Query OK, 0 rows affected (0.00 sec)
rw_shell@mysql 17:22: [(none)]>
rw_shell@mysql 17:22: [(none)]>select @@hostname;
+---------------+
| @@hostname |
+---------------+
| 172-16-104-57 |
+---------------+
1 row in set (0.01 sec)
从上面的实验,我们可以看到,默认情况下,如果执行读取操作,我们将到达复制节点,但如果启动事务,我们将到主节点,而无需更改端口并使用相同的连接。
rw_shell@mysql 17:25: [(none)]>select @@hostname;
+---------------+
| @@hostname |
+---------------+
| 172-16-104-57 |
+---------------+
1 row in set (0.01 sec)
rw_shell@mysql 17:25: [(none)]>start transaction read only;
Query OK, 0 rows affected (0.02 sec)
rw_shell@mysql 17:26: [(none)]>select @@hostname;
+---------------+
| @@hostname |
+---------------+
| 172-16-104-57 |
+---------------+
1 row in set (0.01 sec)
启动事务类型为只读事务的话,也会直接到复制节点。
rw_shell@mysql 17:32: [(none)]>ROUTER SET access_mode='read_only';
Query OK, 0 rows affected (0.00 sec)
rw_shell@mysql 17:32: [(none)]>select @@hostname;
+---------------+
| @@hostname |
+---------------+
| 172-16-104-57 |
+---------------+
1 row in set (0.02 sec)
rw_shell@mysql 17:33: [(none)]>ROUTER SET access_mode='read_write';
Query OK, 0 rows affected (0.00 sec)
rw_shell@mysql 17:33: [(none)]>select @@hostname;
+---------------+
| @@hostname |
+---------------+
| 172-16-104-56 |
+---------------+
1 row in set (0.02 sec)
可以通过 ROUTER SET 语句,定义接下来 SQL 访问类型。
MySQL 8.2 版本的 router 读写分离支持使用一个端口,无需业务适应更改,是一项非常很有价值的功能,可以优化数据库性能和可扩展性,而无需对应用程序进行任何更改。
参考资料:https://lefred.be/content/mysql-8-2-transparent-read-write-splitting/