MySQLReplicaSet 安装原创
# 1. 部署环境及初始化
为了简单起见,建议用yum方式标准化安装MySQL Shell、Router
- MySQL官方的yum源,需要先下载安装repo包,下载地址:
# EL7;EL8/EL9 请把 el7 换成对应版本,repo 包版本号也会随官方更新,先到 dev.mysql.com/downloads/repo/yum/ 确认
yum -y install https://dev.mysql.com/get/mysql80-community-release-el7-4.noarch.rpm
2
- 用yum安装MySQL相关的软件包了
yum list|grep mysql-shell
yum install -y mysql-shell mysql-router
#也可以用rpm包安装
wget https://downloads.mysql.com/archives/get/p/43/file/mysql-shell-8.0.34-1.el7.x86_64.rpm
wget https://downloads.mysql.com/archives/get/p/41/file/mysql-router-community-8.0.34-1.el7.x86_64.rpm
2
3
4
5
6
7
- 网络调整
# 放行集群内部互访(MySQL 端口 + Group Replication 通信端口)
for ip in 192.0.2.11 192.0.2.12 192.0.2.13; do
sudo iptables -I INPUT -s ${ip}/32 -p tcp -m multiport --dports 3366,33660 -j ACCEPT
done
sudo service iptables save
2
3
4
5
为什么要单独放行 33660:Group Replication 走的是独立于 MySQL 端口的组通信端口(group_replication_local_address 指定,惯例是 MySQL 端口后加 0 或另取一段),只放行 3366 会导致节点能连上、但加入组时卡在 RECOVERING 一直不进 ONLINE——这是新搭集群最常见的一个卡点。
- CPU磁盘内存调整
echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo 'echo never > /sys/kernel/mm/transparent_hugepage/defrag' >> /etc/rc.d/rc.local
cat /sys/block/sdb/queue/scheduler
echo noop >/sys/block/sdb/queue/scheduler
cpupower frequency-info --governors
cpupower frequency-set --governor performance
cpupower frequency-info --policy
2
3
4
5
6
7
8
9
10
11
- 如果用的是 GreatSQL 分支,它依赖 jemalloc,需要先装上(官方 MySQL 不需要这一步,可跳过)
yum -y install jemalloc jemalloc-devel
#检查
[root@db-01]# ldconfig
[root@db-01]# ldconfig -p | grep libjemalloc
2
3
4
5
- /etc/hosts绑定IP和主机名防止创建mgr时报错 192.0.2.11 mytest-dbtest01 192.0.2.12 mytest-dbtest02 192.0.2.13 mytest-dbtest03
# 安装mysql
- 步骤省略。
- 修改root密码
SET SQL_LOG_BIN=0;
alter user 'root'@'localhost' identified by '';
rename user 'root'@'localhost' to 'admin'@'localhost';
SET SQL_LOG_BIN=1;
2
3
4
5
6
7
- 避免使用root请创建mgr_admin用户
create user `dba_admin`@`192.0.2.%` identified by '';
GRANT ALL PRIVILEGES ON *.* TO `dba_admin`@`192.0.2.%` WITH GRANT OPTION;
-- 注:AdminAPI 的建集群操作确实需要很高的权限,官方也是这么建议的。
-- 但这个账号只应对集群内网段开放(上面的 192.0.2.%),且不要复用为业务账号。
2
3
4
5
# 利用 MySQL Shell 构建 ReplicaSet
⚠️ ReplicaSet 不是 MGR。 下面这套 AdminAPI 流程和 innodb cluster 安装 长得几乎一样,但底层完全不同:InnoDB Cluster 底下是 Group Replication(多数派协议,可自动故障转移),ReplicaSet 底下就是传统异步主从——它只是给主从复制套了一层统一的纳管接口,没有自动故障转移。主库挂了必须有人(或有脚本)去执行
forcePrimaryInstance。选它的理由是简单、资源省、对网络要求低;代价是 RTO 取决于人/脚本的反应速度,且异步复制天然有丢数据的风险。要自动切换见 ReplicaSet 高可用配置。
- 登录第一台mysql
mysqlsh --uri dba_admin@192.0.2.11:3366
- 检查配置
dba.configureInstance();
dba.checkInstanceConfiguration('dba_admin@192.0.2.12:3366');
dba.checkInstanceConfiguration('dba_admin@192.0.2.13:3366');
2
3
- 创建集群
var rs = dba.createReplicaSet("My_replicaset")
如果失败或报错需要重来:
dba.dropMetadataSchema();
⚠️ 这条只能在「刚创建失败、还没有任何业务数据」的实例上用。它会删掉 mysql_innodb_cluster_metadata 元数据库——在一个已在运行的集群上执行,等于把集群拓扑信息整个抹掉,Router 会立刻失去路由目标,且无法通过 dba.getCluster() 恢复,只能重新 createCluster 并逐个 addInstance。误操作代价极高,执行前先确认 cluster.status() 里没有任何在服务的节点。
- 添加节点
rs.addInstance('dba_admin@192.0.2.12:3366');
选择默认克隆模式添加直接回车 重复以上操作添加所有节点
- 查看状态以下命令均可查看 首先定义var
var rs = dba.getReplicaSet()
然后执行
rs.status();
rs.status({extended:1});
2
- 手动切换主从
var rs = dba.getReplicaSet()
rs.setPrimaryInstance('192.0.2.12:3366') # 主库运行时,可执行。否则会有以下报错
rs.forcePrimaryInstance('192.0.2.12:3366') # 强制切主:仅在原主库确认已不可用时使用
2
3
4
ERROR: Unable to connect to the PRIMARY of the ReplicaSet Core_rs: MYSQLSH 51118: Could not open connection to '192.0.2.11:3366': Can't connect to MySQL server on '192.0.2.11:3366' (110)
- 移除节点
⚠️ forcePrimaryInstance 是有数据丢失风险的操作。ReplicaSet 是异步复制,被选中的从库可能还没收全原主库的 binlog,切过去就等于丢掉那部分事务。执行前如果原主库还能连上,一律先用 setPrimaryInstance(它会等待从库追平再切,无损);只有原主库彻底失联时才用 force。
切之前先看清楚各节点落后多少:
rs.status({extended:1}); // 看每个 SECONDARY 的 replicationLag
切完必须做的一件事:原主库恢复后不能直接开机接回。它上面很可能有没同步出去的事务,直接加回来会造成 GTID 分叉(ERROR 3546 或数据不一致)。正确做法是把它当成一个全新节点,用 clone 方式重新加入:
rs.rejoinInstance('192.0.2.11:3366'); // 若报 GTID 分叉,改用下面这条
rs.removeInstance('192.0.2.11:3366', {force:true});
rs.addInstance('dba_admin@192.0.2.11:3366', {recoveryMethod:'clone'});
2
3
rs.removeInstance('192.0.2.11:3366') # 手动移除节点,节点已宕机的情况会报错
ERROR: Unable to connect to the target instance 192.0.2.11:3366. Please make sure the instance is available and try again. If the instance is permanently not reachable, use the 'force' option to remove it from the replicaset metadata and skip reconfiguration of that instance.
2
rs.removeInstance('192.0.2.11:3366',{force:true}) # 强制移除宕机节点
NOTE: Unable to connect to the target instance 192.0.2.11:3366:3366. The instance will only be removed from the metadata, but its replication configuration cannot be updated. Please, take any necessary actions to make sure that the instance will not replicate from the replicaset if brought back online.
2
3
# 部署MySQL Router,实现读写分离以及故障自动转移
MySQL Router是一个轻量级的中间件,它采用多端口的方案实现读写分离以及读负载均衡,而且同时支持mysql和mysql x协议。
mysqlrouter初始化 MySQL Router对应的服务器端程序文件是 /usr/bin/mysqlrouter,第一次启动时要先进行初始化
# 参数解释
参数 --bootstrap 表示开始初始化
参数 dba_admin@192.0.2.11:3366 是MGR集群管理员账号
--user=mysqlrouter 是运行mysqlrouter进程的系统用户名
[root@db-01]# mysqlrouter --bootstrap dba_admin@192.0.2.11:3366 --user=mysqlrouter
Please enter MySQL password for dba_admin: <-- 输入密码
# 然后mysqlrouter开始自动进行初始化
# 它会自动读取MGR的元数据信息,自动生成配置文件
2
3
4
5
4.2 启动mysqlrouter服务 这就初始化完毕了,按照上面的提示,直接启动 mysqlrouter 服务即可:
[root@db-01]# systemctl start mysqlrouter
检查
ps -ef | grep -v grep | grep mysqlrouter
netstat -lntp | grep mysqlrouter
2
3
4
5
6
7
mysqlrouter 初始化时自动生成的配置文件是 /etc/mysqlrouter/mysqlrouter.conf,主要是关于R/W、RO不同端口的配置
[routing:myCluster_rw]
bind_address=0.0.0.0
bind_port=6446
destinations=metadata-cache://myCluster/?role=PRIMARY
routing_strategy=first-available
protocol=classic
2
3
4
5
6
可以根据需要自行修改绑定的IP地址和端口。
4.3 确认读写分离效果 现在,用客户端连接到6446(读写)端口,确认连接的是PRIMARY节点:
[root@db-01]# mysql -h192.0.2.11 -u dba_admin -p -P6446
mysql>select @@server_uuid;
2
3
4.4 关闭ssl避免跟客户端应用不兼容通讯协议
分别登录mysqlrouter 的服务器,更改配置
sudo vim /etc/mysqlrouter/mysqlrouter.conf
client_ssl_mode=DISABLED
server_ssl_mode=DISABLED
server_ssl_verify=DISABLED
2
3
4
5
分别重启mysqlrouter
sudo systemctl restart mysqlrouter.service
4.5 从元数据库剔除mysqlrouter节点
var rs = dba.getReplicaSet()
rs.listRouters()
rs.removeRouterMetadata('192.0.2.11::system');
2
3
4
# 验证
var rs = dba.getReplicaSet();
rs.status({extended:1});
2
预期输出(关注 status、instanceRole 与 replicationLag):
{
"replicaSet": {
"name": "My_replicaset",
"primary": "192.0.2.11:3366",
"status": "AVAILABLE",
"statusText": "All instances available.",
"topology": [
{"address": "192.0.2.11:3366", "instanceRole": "PRIMARY", "status": "ONLINE"},
{"address": "192.0.2.12:3366", "instanceRole": "SECONDARY", "status": "ONLINE",
"replication": {"applierStatus": "APPLIED_ALL", "replicationLag": null}}
]
}
}
2
3
4
5
6
7
8
9
10
11
12
13
replicationLag 为 null 且 applierStatus 是 APPLIED_ALL 表示已追平。出现秒数说明有延迟,切主前必须等它归零。
端到端确认读写分离生效:
mysql -h 192.0.2.11 -P6446 -u dba_admin -p -e "SELECT @@hostname, @@read_only;" # 期望 read_only=0
mysql -h 192.0.2.11 -P6447 -u dba_admin -p -e "SELECT @@hostname, @@read_only;" # 期望 read_only=1
2
# 坑与边界
- ReplicaSet 没有自动故障转移,这是它与 InnoDB Cluster 的根本区别,也是选型时唯一真正要想清楚的一条。别因为部署流程相似就以为高可用能力也相似。
- 异步复制会丢数据。主库提交后立刻宕机,未传出的 binlog 就丢了。对丢数据零容忍的场景应该上 InnoDB Cluster(多数派确认),或至少开半同步复制。
- Router 的 6447 只读口在只有 2 个节点时没有冗余:唯一的 SECONDARY 挂掉后所有读流量会失败或全压到 PRIMARY 上,容量规划时要按「读流量最终全落到主库」来算。
removeInstance({force:true})只改元数据,不会去关停那个实例的复制。被强制移除的节点如果还活着,它会继续从原主库拉数据——必须手工上去STOP REPLICA; RESET REPLICA ALL;,否则它会变成一个没人管、却还在同步的影子从库。- 所有节点的
server_id必须唯一,且建议显式设置report_host(用 IP 或可解析的主机名)。不设时 AdminAPI 会用系统 hostname,在/etc/hosts没配全的环境里会连不上。