目录
- 背景
 - mysql8安装
 - 备机安装mysql8
- 主备状态查看
 
 - mysql router安装
 - keepalived安装
 - proxy安装
 - MHA安装
 - my.cnf配置
 - Granafa监控
 - 主从同步处理
- 主从error处理
- pt-table-sync安装
 - 手动处理
 - 自动处理
 
 - 主挂了从升主处理
 - 主挂了从升主之后,原主恢复变从
 
 - 主从error处理
 
背景
- centos7.6机器,256G内存,64C,3.5T SSD
 - 之前线上已经有mysql运行中的机器了,所以会在原有基础上修改/etc/my.cnf配置文件
 - 目前架构是一主一台,两个集群,数据是应用层双写,业务不太敏感数据轻微不一致
 - 使用mysql router做集群内的读写分离,两台都安装router,并使用vip做router的高可用
 - 没用使用MHA等方案监控自动主从切换,当主挂了时,人工介入目前,后续可能考虑
 - 
架构,单集群展示,VIP会漂移到Router A或者Router B,然后根据读写到mysqlA或者mysqlB
image.png 
mysql8安装
- 安装repo, 注意看有没报错哈,没报错就我截图那样
 
yum install -y https://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.rpm

image.png
- 安装mysql8, 注意看有没报错哈,没报错就我截图那样
 
yum install -y --nogpgcheck mysql-community-server

image.png
- 看下机器之前有没mysql数据, 有的话考虑rm -rf掉
 
ll /var/lib/mysql/
- 我是在本地编辑my.cnf然后上传上去的,所以我这边执行删除之前旧的my.cnf,再上传,如果你使用默认的就忽略就好
 
cd /etc
rm my.cnf
rz 上传本地编辑的文件
chmod 644 /etc/my.cnf
- 我的SSD盘是/cache1盘,my.inf配置了对应数据盘
 
mkdir -p /cache1/mysql_data
chmod 750 /cache1/mysql_data
- 开启mysql并自启动,没报错就是启动成功,这种启动方式能自动重启, kill -9可以测试
 
systemctl start mysqld
systemctl enable mysqld
- 改密码, 先看机器上初始密码,我的是qMP-Vqed_6ec,我的密码是没有特殊符合不符合安全规则,我是先随便设置了个符合的然后SET GLOBAL validate_password.special_char_count = 0;再改回来
 
sudo grep 'temporary password' /cache1/mysql_data/*.err
mysql -uroot -pqMP-Vqed_6ec
ALTER USER 'root'@'localhost' IDENTIFIED BY 'xxxx';
- 配置备库读取权限
 
# 主库执行
CREATE USER 'repl_user'@'备库' IDENTIFIED BY '密码';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'备库';
FLUSH PRIVILEGES;
备机安装mysql8
- 步骤基本跟上面一致,就是my.cnf配置备注部分要改
 - 也配置下读取备库(万一主从切换)权限
 
# 备库执行
CREATE USER 'repl_user'@'主库' IDENTIFIED BY '密码';
GRANT REPLICATION SLAVE ON *.* TO 'repl_user'@'主库';
FLUSH PRIVILEGES;
- 备库要执行开启同步
 
CHANGE MASTER TO
MASTER_HOST='主库ip',
MASTER_PORT=设置的端口,
MASTER_USER='repl_user',
MASTER_PASSWORD='密码',
MASTER_AUTO_POSITION=1;
START SLAVE;
SHOW SLAVE STATUS\G;
主备状态查看
- 查看主备状态具体
 
# 看下有没erro
1. SHOW SLAVE STATUS\G;
# 有error可能具体报错
SELECT * FROM performance_schema.replication_applier_status_by_worker\G;
mysqlrouter安装
- 安装命令,注意看有没报错, 中途记得输入y
 
wget https://repo.mysql.com//mysql80-community-release-el7-7.noarch.rpm
sudo rpm -ivh mysql80-community-release-el7-7.noarch.rpm
sudo yum clean all
sudo yum install mysql-router --nogpgcheck

image.png
- 查看版本
 
mysqlrouter --version

image.png
- 修改配置文件
 
vim /etc/mysqlrouter/mysqlrouter.conf
- 在某尾加上
 
# 写路由,所有写流量只走主库
[routing:write]
bind_address = 0.0.0.0:7001
mode = read-write
destinations = 主库:xxx
# 读路由,优先走从库,从库挂了才能fallback到主库,当然读也可以不走主库,另外拓展出一台读机器,因为给的资源有限制,主库要拿来读了
[routing:read]
bind_address = 0.0.0.0:7002
mode = read-only
destinations = 从库:xxx,主库:xxx
- 启动
 
sudo systemctl start mysqlrouter
sudo systemctl enable mysqlrouter
- 查看效果
 
sudo netstat -lntp | grep mysqlrouter
image.png

image.png
keepalived安装
- 考虑到mysqlrouter单点故障,这边使用keepalived做高可用
 - 安装, 中间记得输入y,注意看有没报错
 
yum install keepalived

image.png
- 看下网卡, 我的是输出eth2,如果主备keepalived机器网卡不同则virtual_ipaddress配置需不同
 
ip link show | grep 'state UP' | awk -F': ' '{print $2}'
- 修改配置文件
 
vim /etc/keepalived/keepalived.conf
- 配置文件数据,mysqlrouter有两台,主的设置如下
 
global_defs {
   router_id MYSQL_ROUTER_MASTER
}
vrrp_script check_mysqlrouter {
    script "killall -0 mysqlrouter"
    interval 2
    weight 2
}
vrrp_instance VI_1 {
    state MASTER
    interface eth2
    virtual_router_id 211
    priority 100
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 你的密码
    }
    track_script {
        check_mysqlrouter
    }
    virtual_ipaddress {
        你的vip地址(我这边是找公司申请的)
    }
}
- 配置文件数据,mysqlrouter有两台,备的设置如下,我的网卡是一致的,如果你的网卡不一致要特殊配置
 
global_defs {
   router_id MYSQL_ROUTER_SLAVE
}
vrrp_script check_mysqlrouter {
    script "killall -0 mysqlrouter"
    interval 2
    weight 2
}
vrrp_instance VI_1 {
    state SLAVE
    interface eth2
    virtual_router_id 211
    priority 99
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass xxx须跟主保持一致
    }
    track_script {
        check_mysqlrouter
    }
    virtual_ipaddress {
        你的vip需要跟主保持一致(我这边是找公司申请的vip)
    }
}
- 启动
 
# 启动服务(所有节点)
systemctl start keepalived
# 设置开机自启
systemctl enable keepalived
# 检查服务状态
systemctl status keepalived

image.png
验证vip功能
# 在主节点停止服务
systemctl stop keepalived
# 在备节点检查VIP(应自动接管),eth2 网要根据你自己的来
watch -n1 "ip addr show eth2 | grep 'inet '"
# 恢复主节点
systemctl start keepalived
# 观察VIP回切(根据preempt配置)
tail -f /var/log/keepalived.log
验证绑定, 注意eth2可能要改:
ip addr show eth2 | grep 'inet '
proxy安装
- 由于mysqlrouter已经符合要求,并且mysqlrouter更加轻量化,故不考虑使用proxy
 
MHA安装
- 目前没考虑自动主备切换,后续有需要再安装
 - 注意MHA 不会自动回切或恢复旧主
 - MySQL InnoDB Cluster中MySQL Group Replication支持旧主重新假如自动修复,带需依赖旧主恢复时间和新主保留二进制数据的时间
 - 大型mysql集群可以用
 
- MySQL InnoDB Cluster
 - PXC多主,但是强一致性数据要求,跨集群有点蛋疼
 - 分布式中间件(如 ProxySQL + 分片)
 - MHA + Keepalived
 
Granafa监控
- 做好监控查看各个指标
 
主从同步处理
主从error处理
- 处理之前需要主从机器上面的router都把备库踢掉
 
pt-table-sync安装
// centos7.6,建议主备两台机器都装上,防止备升主,主降备
sudo yum install [https://repo.percona.com/yum/percona-release-latest.noarch.rpm](https://repo.percona.com/yum/percona-release-latest.noarch.rpm) 
sudo percona-release enable tools release
sudo yum install percona-toolkit
pt-table-sync --version
手动处理
- 看下error的地方,大部分的错误是从库不小心写入数据
 
// 看下error
SHOW SLAVE STATUS\G;
// 看具体error
SELECT * FROM performance_schema.replication_applier_status_by_worker\G;
- 跳过某个异常, 可能有多个, 跳过后注意看异常的表,然后执行查看具体哪张表问题,再处理,有可能下面要循环多次以解决异常
 
STOP SLAVE;
// 这个gtid是你从 performance_schema.replication_applier_status_by_worker看到具体的错误来的
SET GTID_NEXT = '14c92d46-3541-11f0-a4f6-90e2ba00f37c:1222';
BEGIN;
COMMIT;
SET GTID_NEXT = 'AUTOMATIC';
START SLAVE;
SHOW SLAVE STATUS\G;
自动处理
- 使用pt-table-sync自动查看,注意有时候数据量太多会卡住,可能某张表太大卡住,这时候就要手动接入处理掉卡住的大表
 
// --table不配置的话就是整个库查看异同
pt-table-sync h=主库host,P=port,u=xxx,p=xxxx \
h=从库host,P=port,u=xxx,p=xxxx \
--database xxx--table xx,xxx \
--print --no-check-slave --no-transaction > repair.sql
- 人工看下repair.sql数据确认下
 - 自动修复
 
// 这里我配置了不可手动写入
STOP SLAVE;
SET GLOBAL super_read_only = OFF;
// 执行修复
mysql -hxxx -Pxxx -uxx -pxxxx 库名< repair.sql
SET GLOBAL super_read_only = ON;
// 确定要跳过的gtid, 看Last_SQL_Error
SHOW SLAVE STATUS\G;
SET GTID_NEXT='xxx:123';  
BEGIN;
COMMIT;
SET GTID_NEXT='AUTOMATIC';
START SLAVE;
SHOW SLAVE STATUS\G;
主挂了从升主处理
1\. 将从库提升为新主库
- vim /etc/my.cnf
将read_only=1 super_read_only=1反向设置都为0
2\. 确认主库故障
- SHOW SLAVE STATUS\G;
- 确保 Slave_IO_Running 和 Slave_SQL_Running 均为 Yes,且 Seconds_Behind_Master 为 0(无延迟)
3\. 停止从库复制进程
在从库执行以下命令,停止复制并解除与原主库的关联
STOP SLAVE;
RESET SLAVE ALL;  -- 清除复制信息
4\. 主备的/etc/mysqlrouter/mysqlrouter.conf,router写库改掉
systemctl restart mysqlrouter
主挂了从升主之后,原主恢复变从
当原主库恢复后,MHA 默认不会自动将其加入集群 ,需要人工干预或配置脚本自动处理。备份新主库数据 (确保安全)。在原主库上重建数据
如果差别不大,比如binlog保存三天,只挂了一天,那就走上面提到的自动处理流程
如果差异比较大执行下面步骤,注意主库数据可以定时备份
1\. 停止并重置从库
STOP SLAVE;
RESET SLAVE ALL; 
2\. 备份主库
# 主库执行(GTID 模式专用)
mysqldump -h xxx -P xx-u xxx -pxxxx \
--single-transaction --set-gtid-purged=ON \
--databases xxx> xxx_full_backup.sql
2.1 传输, 公司传输需要通过跳板机
# 跳板机执行拉
scp -P xx [root@x](mailto:root@xxx)xx:/root/xxx_full_backup.sql /home/xxxx
# 跳板机执行推
scp -P xx  xx_full_backup.sql [root@x](mailto:root@xxx)xxx:/root
# 避免重复
rm xxxx_full_backup.sql 
3\. 清空从库 xxx库, 执行前主从都要将router read去掉从库
-- 从库执行
SET GLOBAL super_read_only = OFF;
DROP DATABASE IF EXISTS xxx;
SET GLOBAL super_read_only = ON;
4\. 导入到从库
# 移除 SET GTID_PURGED 语句
sed '/GTID_PURGED/d' XXX_full_backup.sql > clean_backup.sql
可以开两个窗口
SET GLOBAL super_read_only = OFF;
mysql -uxxx -pxxxx < clean_backup.sql
SET GLOBAL super_read_only = ON;
5\. 重新链接
-- 从库执行
grep 'GTID_PURGED' xxx_full_backup.sql
STOP SLAVE;
RESET MASTER;
SET GLOBAL GTID_PURGED = 'ea6a933c-5647-11f0-afa0-e4434bb74caa:1-1485';
CHANGE MASTER TO
  MASTER_HOST = 'xxx',
  MASTER_PORT = xxx,
  MASTER_USER = 'xxx',
  MASTER_PASSWORD = 'xxxxxxx',
  MASTER_AUTO_POSITION = 1;
START SLAVE;
SHOW SLAVE STATUS\G;
my.cnf配置
- 贴出 centos7.6机器,256G内存,64C,3.5T SSD对应的配置,主库,从库根据注释改改就好, 线上机器是join 临时表量很大超级多,所以按照下面配置了
 
[mysqld]
# 只同步 mydb 这个数据库
replicate-wild-do-table=xxx.%
# 不同步xxx.test
#replicate-ignore-table=xxx.test
# 兼容旧版本
default_authentication_plugin=mysql_native_password 
max_connections=3500
tmp_table_size=17179869184
max_heap_table_size=17179869184
table_open_cache=8192
innodb_buffer_pool_size=171798691840
innodb_io_capacity_max=20000
innodb_io_capacity=10000
binlog_cache_size=33554432
#expire_logs_days=1 已废弃
binlog_expire_logs_seconds=86400
long_query_time=30
innodb_flush_neighbors=0
lower_case_table_names=0
binlog_ignore_db=scheduler_task
performance_schema_instrument=wait/lock/metadata/sql/mdl=ON
default_authentication_plugin=mysql_native_password
#query_cache_size=0 已废弃
#query_cache_type=0 已废弃
relay_log_info_repository=TABLE
master_info_repository=TABLE
explicit_defaults_for_timestamp=1
key_buffer_size=4G
server_id=12502990
report_port=ss
report_host=你的ip
port=ss
datadir=/cache1/mysql_data
socket=/cache1/mysql_data/mysql.sock
log_error=/cache1/mysql_data/mysql_error.err
pid_file=/cache1/mysql_data/mysqld.pid
slow_query_log=1
slow_query_log_file=slow_query.log
#log_bin=mysql-bin
relay_log=mysql-relay-bin
relay_log_space_limit=10737418240
general_log=0
general_log_file=mysql-general.log
binlog_format=ROW
sync_binlog=0
log_slave_updates=1
# 主库两个都设置0
read_only=0
super_read_only=0
event_scheduler=0
transaction_isolation=REPEATABLE-READ
max_allowed_packet=64M
sort_buffer_size=32M
read_buffer_size=64M
read_rnd_buffer_size=32M
join_buffer_size=32M
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
group_concat_max_len=64K
innodb_stats_on_metadata=0
innodb_file_per_table=1
thread_cache_size=1024
skip_external_locking
skip_name_resolve
innodb_log_file_size=256M
innodb_flush_log_at_trx_commit=2
innodb_log_buffer_size=16M
character_set_server=utf8
log_bin_trust_function_creators=1
open_files_limit=60000
secure_file_priv=''
slave_net_timeout=30
binlog_error_action=IGNORE_ERROR
#show_compatibility_56=on
#log_warnings=1
optimizer_switch='semijoin=OFF'
gtid_mode=ON
enforce_gtid_consistency=ON
mysqlx=0
[mysql]
no_auto_rehash
prompt="\u \d>"
[mysqldump]
quick
[client]
socket=/cache1/mysql_data/mysql.sock
