MySQL高可用方案--Xenon全解

MySQL高可用方案 Xenon全解

MySQL是云计算领域非常重要的RDS(Relational Database Service),应用非常广泛,但是MySQL的运维非常复杂。为了提供更好的服务,我们开发了Xenon。它帮助 MySQL Cluster 更高的可用性,使强一致性达到一个新的高度。由于高度自动化且无需人工干预,O&M(运维)现在更容易且成本更低。

Xenon 是一个去中心化的代理,不会对 MySQL 源进行侵入式访问。Xenon管理 MySQL 实例。只要网络可达,它就不会关心部署站点。

它使用 LVS + Raft + GTID 并行复制进行主数据同步。更重要的是,Xenon拯救了一批运维人员。现在他们最大的乐趣就是在制作休闲高手。

Xenon是一个 MySQL 复制拓扑 HA、管理和可视化工具,允许:

  • 发现

Xenon主动获取集群拓扑并映射它们。它读取基本的 MySQL 信息,例如复制状态和配置。

  • 重构

Xenon了解复制规则。它知道 binlog 文件:位置、GTID、Binlog 服务器。

重构复制拓扑可以是将副本拖放到另一个主服务器下。移动副本是安全的:xenon将拒绝非法的重构尝试。

  • 恢复

Xenon使用整体方法来检测主控和中间主控故障。根据从拓扑本身获得的信息,它可以识别各种故障场景。

可选地,它具有恢复节点的选项(这也允许用户指定恢复节点)。

Raft+介绍

Raft+中,使用 MySQL GTID (Global Transaction Identifier) 作为日志索引,用于Raft protocol结合 MySQL 的 Multi-Threaded Slave (MTS)。它可以完成日志条目的并行复制、并行回放,日志回放消耗的时间极短,并且在故障转移后立即对外服务。

同时,Raft+使用Semi-Sync-Replication保证至少有一个slave与master完全同步。master失败后,数据完全同步的slave将被选为新的Master。

这确保了零数据丢失和高可用性。

Raft+ 只读状态

除了Leader// raft+之外的三个状态还提供Candidate状态:Follower Idle

  • **空闲状态:**不参与选举Lord但会感知Leader变化改变复制通道。该Idle状态适合部署为远程机房的容灾实例。

通过Idle设置,可以重新组装不同的Xenon节点来提供服务,我们称之为Semi-Raft Group.

例如,机房 A 有 3 个节点,形成一个Semi-Raft Group. 这些机器是:

[A1:Leader, A2:Follower, A3: Follower]

Room B有3个容灾节点(Semi-Raft Group):

[B1:Idle, B2:Idle, B3:Idle]

如果房间A断电并恢复很长时间,我们可以设置房间B的三个实例从Idle到Follower。

这样B房间的Semi-Raft Group就向主机发起外部服务的选择。结合BinlogServer,A 的数据一模一样。

MySQL高可用方案--Xenon全解_第1张图片

运行条件

  • Xenon是一个独立的二进制文件,只能在linux上运行。
  • Xenon 必须开启GTID semi-sync并行复制技术,最好是 MySQL 版本 5.7 or higher。
  • 需要Go 1.8版本 或更高版本。
  • 必须使用mysqld_safe启动MySQL。
  • 参加选举的机器数量为奇数,防止脑裂的发生。
  • 必须开启ssh互信。
  • 使用rebuildme重建必须安装xtrabackup。

补充:

此软件需要在每台MySQL服务器安装配置。

安装常用命令

所有机器执行

yum install -y wget git unzip sshpass

配置免密登录

所有机器执行

# 生成密钥
ssh-keygen # 一路回车

# 传输密钥
ssh-copy-id root@'10.10.8.203'
ssh-copy-id root@'10.10.8.204'
ssh-copy-id root@'10.10.8.206'

# 验证
ssh 10.10.8.203 date
ssh 10.10.8.204 date
ssh 10.10.8.206 date

配置时间同步

内网可指定内网时间同步服务器,此处使用阿里云时间同步服务器。

# 时间同步
ntpdate  -u  ntp.aliyun.com

# 定时同步时间
cat >>/var/spool/cron/root<<'EOF'
# ntp 时间同步
00 00 * * * /usr/sbin/ntpdate  -u  ntp.aliyun.com
EOF

# 验证
crontab -l

安装golang

所有机器执行

# 下载二进制包
wget https://golang.google.cn/dl/go1.18.8.linux-amd64.tar.gz

# 解压包
rm -rf /usr/local/go && tar -C /usr/local -xzf go1.18.8.linux-amd64.tar.gz

# 配置环境变量
cat >/etc/profile.d/go.sh<<'EOF'
export PATH=$PATH:/usr/local/go/bin
EOF

# 生效环境变量
source  /etc/profile.d/go.sh

# 关闭GO111MODULE
go env -w GO111MODULE=off

# 查看版本信息
go version

安装xtarbackupex

所有机器执行

# 下载
wget  https://downloads.percona.com/downloads/Percona-XtraBackup-2.4/Percona-XtraBackup-2.4.26/binary/redhat/7/x86_64/percona-xtrabackup-24-2.4.26-1.el7.x86_64.rpm

# 安装
yum install  -y  percona-xtrabackup-24-2.4.26-1.el7.x86_64.rpm && rm -f  percona-xtrabackup-24-2.4.26-1.el7.x86_64.rpm

# 验证
xtrabackup --version

安装mysql5.7

所有机器执行

# 安装常用命令及依赖
yum install   -y  net-tools lrzsz  vim  libaio-devel ntpdate  epel-release \
tree  libaio-devel lsof sysstat  bash-completion  bash-completion-extras jemalloc

# 卸载mariadb
yum remove  mariadb*  -y

# 创建MySQL软件及数据目录
mkdir  /mysql/{tools,tar}  -p
mkdir  /mydata/3306/{binlog,data,etc,lock,log,pid,socket,tmp,script}  -p

# 下载安装包

# md5验证包的完整性
md5sum mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz
# 1748ec2c8b5ca1bcf8ba3b1f5e956139  mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz

# 解压MySQL包
cd      /mysql/tar
tar xf  /mysql/tar/mysql-5.7.36-linux-glibc2.12-x86_64.tar.gz

# 移动tar包到mysql目录
mv /mysql/tar/mysql-5.7.36-linux-glibc2.12-x86_64  /mysql/mysql57

# 配置环境变量
cat  >/etc/profile.d/mysql.sh<<'EOF'
export PATH=/mysql/mysql57/bin:$PATH
EOF
#修改权限
chmod 700 /etc/profile.d/mysql.sh
#生效环境变量
source /etc/profile.d/mysql.sh

# 创建所需用户
groupadd  mysql
useradd mysql -g mysql

# 添加配置文件
vim /mydata/3306/etc/my.cnf
[client]
socket=/mydata/3306/socket/mysql.sock
[mysql]
socket=/mydata/3306/socket/mysql.sock
prompt='\u@\d>\_'
default-character-set=utf8mb4
no-auto-rehash
#show-warnings  #查看告警信息

[mysqld]
# GENERAL #
lower_case_table_names         = 1
character_set_server           = utf8mb4
collation_server               = utf8mb4_general_ci
user                           = mysql
default_storage_engine         = InnoDB
port                           = 3306
socket                         = /mydata/3306/socket/mysql.sock
pid-file                       = /mydata/3306/pid/mysql.pid
log_timestamps                 = SYSTEM
default_time_zone              = +08:00
secure_file_priv               = ''
local_infile                   = ON
thread_stack                   = 512K
# MyISAM #
key_buffer_size                = 128M
myisam_sort_buffer_size        = 8M
read_rnd_buffer_size           = 262144

# SAFETY #
back_log                       = 1024
bind-address                   = 0.0.0.0
innodb_strict_mode             = 1
max_allowed_packet             = 32M
max_connect_errors             = 1000000
interactive_timeout            = 900
wait_timeout                   = 900
skip_grant_tables              = 0
skip_name_resolve              = 1
host_cache_size                = 0
sql_mode                       = NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
sysdate_is_now                 = 1
transaction_isolation          = REPEATABLE-READ
explicit_defaults_for_timestamp = 1

# DATA STORAGE #
basedir                        = /mysql/mysql57
datadir                        = /mydata/3306/data
tmpdir                         = /mydata/3306/tmp

# BINARY LOGGING #
#max_binlog_files              = 100
binlog_cache_size              = 1M
binlog_format                  = ROW
expire_logs_days               = 7
max_binlog_size                = 512M
max_binlog_cache_size          = 4G
log_bin                        = /mydata/3306/binlog/mysql-bin
log_bin_index                  = /mydata/3306/binlog/mysql-bin.index
sync_binlog                    = 1
innodb_support_xa              = 1
master-info-repository         = TABLE

# REPLICATION #
log_slave_updates              = 1
read_only                      = 1
relay_log                      = /mydata/3306/binlog/relay-bin
relay_log_index                = /mydata/3306/binlog/relay-bin.index
server_id                      = 330651
slave_skip_errors              = OFF
skip_slave_start
slave_net_timeout              = 60
relay-log-info-repository      = TABLE
slave_compressed_protocol      = OFF   #5.6版本、5.7.21+、8.0.4+可以设置为ON

# CACHES AND LIMITS #
max_connections                = 2000
max_heap_table_size            = 128M
tmp_table_size                 = 128M
open_files_limit               = 65535
query_cache_limit              = 1M
query_cache_min_res_unit       = 4096
query_cache_size               = 0
query_cache_type               = 0
join_buffer_size               = 1M
read_buffer_size               = 1M
sort_buffer_size               = 1M
table_definition_cache         = 4096
table_open_cache               = 4000
thread_cache_size              = 1000

# INNODB #
innodb_autoextend_increment    = 64
innodb_buffer_pool_instances   = 45
innodb_buffer_pool_size        = 95G
innodb_buffer_pool_load_at_startup  = ON
innodb_buffer_pool_dump_at_shutdown = ON
innodb_data_file_path          = ibdata1:1024M:autoextend
innodb_fast_shutdown           = 1
innodb_file_format             = Barracuda
innodb_file_format_max         = Barracuda
innodb_file_per_table          = 1
innodb_force_recovery          = 0
innodb_flush_log_at_trx_commit = 1
innodb_flush_method            = O_DIRECT
innodb_lock_wait_timeout       = 50
innodb_log_buffer_size         = 8M
innodb_log_file_size           = 1024M
innodb_log_files_in_group      = 4
innodb_open_files              = 10000
innodb_purge_threads           = 4
innodb_read_io_threads         = 8
innodb_write_io_threads        = 8
innodb_sync_spin_loops         = 30
innodb_thread_concurrency      = 24
innodb_print_all_deadlocks     = 1
innodb_io_capacity             = 2000

# LOGGING #
general_log                    = 0
general_log_file               = /mydata/3306/log/mysql-general.log
log_error                      = /mydata/3306/log/mysql-error.log
log_queries_not_using_indexes  = 0
long_query_time                = 1
slow_query_log                 = 1
slow_query_log_file            = /mydata/3306/log/mysql-slow.log

# GTID
enforce_gtid_consistency       = ON
gtid_mode                      = ON
binlog_gtid_simple_recovery    = TRUE

# SEMI-SYNC
plugin-load = "rpl_semi_sync_master=semisync_master.so;rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_master_enabled = ON
rpl_semi_sync_slave_enabled = ON
rpl_semi_sync_master_timeout = 10000
rpl_semi_sync_master_trace_level = 32
rpl_semi_sync_slave_trace_level = 32
rpl_semi_sync_master_wait_no_slave = ON

##parallel 配置文件参数 ##
slave_rows_search_algorithms            = 'INDEX_SCAN,HASH_SCAN'
binlog_group_commit_sync_delay          = 500
binlog_group_commit_sync_no_delay_count = 13
slave_parallel_type                     = LOGICAL_CLOCK
slave_parallel_workers                  = 40
slave_preserve_commit_order             = 1
binlog_transaction_dependency_tracking  = writeset
transaction_write_set_extraction        = XXHASH64
slave_checkpoint_period                 = 2
binlog_checksum                         = CRC32
log_slave_updates                       = ON
master_info_repository                  = TABLE
relay_log_info_repository               = TABLE
relay_log_recovery                      = 1


# 将配置文件链接到 etc 下
ln -s /mydata/3306/etc/my.cnf  /etc/my3306.cnf

# 创建error文件
touch /mydata/3306/log/mysql-error.log

# 授权mysql数据目录
chown -R mysql:mysql /mydata/
chmod 700 /mydata/

# 初始化MySQL
mysqld  --initialize-insecure  --user=mysql --basedir=/mysql/mysql57 --datadir=/mydata/3306/data  --innodb_data_file_path=ibdata1:1024M:autoextend

# 配置system管理
cat  >/usr/lib/systemd/system/mysqld3306.service<<'EOF'
[Unit]
Description=MySQL 3306  Server
After=network.target
After=syslog.target

[Install]
WantedBy=multi-user.target

[Service]
User=mysql
Group=mysql
ExecStart=/mysql/mysql57/bin/mysqld_safe --defaults-file=/mydata/3306/etc/my.cnf
LimitNOFILE = 65536
LimitNPROC = 65535
Restart=on-failure
RestartPreventExitStatus=1
ReStartSec=10
StartLimitInterval=300
StartLimitBurst=2
TimeoutStartSec=30
TimeoutStopSec=30
EOF


# 生效
systemctl daemon-reload

# 启动实例
systemctl start mysqld3306

# 重置启动次数
#systemctl reset-failed mysqld3306.service

# 查看状态
systemctl status  mysqld3306

# 设置便捷登录
cat  >/usr/local/bin/3306_mysql_login.sh<<'EOF'
mysql -A -S /mydata/3306/socket/mysql.sock -p
EOF

# 授权
chmod 700 /usr/local/bin/3306_mysql_login.sh

# 修改密码
alter user root@localhost identified by '123456';
flush  privileges;

搭建主从

主库创建用户后,在从库服务器执行指定主库和开启主从的操作。

# 主库创建复制用户
grant replication slave  on *.*  to repl@'10.10.8.%' identified by '123456';
flush privileges;


# 从库指定主库信息
change master to
master_host='10.10.8.203',
master_user='repl',
master_port=3306,
master_password='123456',
MASTER_AUTO_POSITION=1;

#开启主从
start slave;
#查看主从信息
show slave status\G

安装xenon

所有机器执行

# 下载
cd /usr/local/
git clone https://github.com/radondb/xenon.git

# 安装
cd xenon
make build
make

# 验证
/usr/local/xenon/bin/xenoncli version
#xenoncli:[{Tag:v1.1.4-34-gbae9e0d Time:2022/11/09 12:04:30 Git:bae9e0d GoVersion:go1.18.8 Platform:linux amd64}]

配置xenon

所有机器执行,按照自己的环境修改配置中的IP地址为本机的IP。

# 进入配置文件目录
cd /usr/local/xenon/conf

# 主库创建xenon账号
#grant all on *.* to xenon@'10.10.8.%' identified by '123456';flush privileges;

# 创建备份目录
mkdir -p /mydata/backup/3306

# 拷贝配置文件
cp xenon-sample.conf.json  xenon.json

# 创建日志目录
mkdir /usr/local/xenon/log

# 编辑配置文件
vim xenon.json
{
     "server":
     {
             //修改为当前服务器的IP
             "endpoint":"10.10.8.203:8801" 
     },
    
     "raft":
     {
             "meta-datadir":"raft.meta",
             "heartbeat-timeout":1000,
             "election-timeout":3000,
             // 修改为自己的VIP、子网掩码和网卡名
             "leader-start-command":"/sbin/ip a add 10.10.8.207/23 dev ens192 && arping -c 3 -A  10.10.8.207  -I ens192",
             "leader-stop-command":"/sbin/ip a del 10.10.8.207/23 dev ens192"
     },
    
     "mysql":
     {
     				 // 数据库连接管理账号(需要在主库创建此账号)
             "admin":"xenon",
             "passwd":"123456",
             "host":"10.10.8.203",
             "port":3306,
             // mysql安装路径
             "basedir":"/mysql/mysql57",
             // mysql配置文件路径
             "defaults-file":"/mydata/3306/etc/my.cnf",
             "ping-timeout":1000,
             "master-sysvars":"super_read_only=0;read_only=0;sync_binlog=default;innodb_flush_log_at_trx_commit=default",
             "slave-sysvars":                 "super_read_only=1;read_only=1;sync_binlog=1000;innodb_flush_log_at_trx_commit=2"
     },
    
     "replication":
     {
             // 指定主从的账号密码
             "user":"repl",
             "passwd":"123456"
     },
    
     "backup":
     {
             // root账号连接信息
             "ssh-host":"10.10.8.203",
             "ssh-user":"root",
             "ssh-passwd":"123456",
             "ssh-port":22,
             // 备份文件保存路径(建议空间大的目录,不要直接使用根下的目录)
             "backupdir":"/mydata/backup/3306",
             // xtrabackupex 安装路径
             "xtrabackup-bindir":"/usr/bin",
             "backup-iops-limits":100000,
             // xtrabackupex 使用内存大小
             "backup-use-memory":  "1GB",
             "backup-parallel":  2
      },
      "rpc":
      {
             "request-timeout":500
      },
    
      "log":
      {
             "level":"INFO"
      }
}

指定VIP

第一次启动需要手动指定VIP

主库执行,按自己的环境需改即可。

/sbin/ip a add 10.10.8.207/23 dev ens192 && arping -c 3 -A  10.10.8.207  -I ens192

启停

# 开启
nohup /usr/local/xenon/bin/xenon -c /usr/local/xenon/conf/xenon.json >>/usr/local/xenon/log/xenon.log 2>&1  &

# 停止
pkill  xenon

加入集群

# 指定配置文件路径 // 不指定报错
echo "/usr/local/xenon/conf/xenon.json"  >/usr/local/xenon/bin/config.path

# 加入集群
/usr/local/xenon/bin/xenoncli cluster \
add 10.10.8.203:8801,10.10.8.204:8801,10.10.8.206:8801

常用集群操作

# 检查集群raft状态
/usr/local/xenon/bin/xenoncli cluster raft

# 检查集群状态
/usr/local/xenon/bin/xenoncli cluster status

# 检查集群mysql状态
/usr/local/xenon/bin/xenoncli cluster mysql

# 检查集群 gtid 状态
/usr/local/xenon/bin/xenoncli cluster gtid

# 删除节点
/usr/local/xenon/bin/xenoncli cluster  \
remove 10.10.8.206:8801

# 重建节点 // 再需要重建的机器上执行
#/usr/local/xenon/bin/xenoncli mysql rebuildme

MySQL操作

# ./xenoncli mysql -h
mysql related commands

Usage:
  xenoncli mysql [command]

Available Commands:
  backup               将这个 mysql 备份到备份目录
  cancelbackup         取消备份
  changepassword       更新 mysql 普通用户密码
  createsuperuser      创建 mysql 超级用户
  createuser           创建 mysql 普通用户
  createuserwithgrants 创建具有权限的 mysql 普通用户
  dropuser             删除 mysql 普通用户
  kill                 杀死 mysql pid (beareful!)
  rebuildme            重建从库 --from=endpoint
  shutdown             关闭 mysql
  start                启动 mysql
  startmonitor         启动 mysqld 监视器
  status               mysql状态 JSON格式现实(mysqld/slave_SQL/IO is running)
  stopmonitor          停止 mysqld 监视器
  sysvar               设置全局变量

MySQL重建详解

当节点出现故障xenon无法自动拉起时,在故障节点使用rebuildme 重建节点是比较简单的。

# 在要做重建的节点执行 
xenoncli mysql rebuildme

# 可指定参数
--from=10.10.8.204:8081   # 指定使用哪个从库进行备份
  • 默认情况下,rebuildme 操作会自动找到与 master 数据备份相同的 slave,因此 master 不会受到太大影响。这不会影响写入业务。
  • 使用--from=IP:XENON_PORT,可以指定从哪个数据库备份。

手动切换主库

当集群需要主库做升级等一些特殊操作时可以选择手动切换主库,以达到串行操作的目的。

在预选的主库(新主库)上执行重新选主的操作。

# 在预选的主库上执行重新选主的操作
/usr/local/xenon/bin/xenoncli raft trytoleader

# 查看状态
/usr/local/xenon/bin/xenoncli  cluster status

# 查看VIP是否切换成功
ip  a|grep 207

MySQL堆栈信息

我们通过 Quickstack 爬取 MySQL 进程,看看 MySQL 是如何调用栈信息的。对问题的后续分析进行了简化。

quickstack速度很快,对流程的影响很小。

# 需要安装quickstack命令,项目地址如下:
https://github.com/yoshinorim/quickstack

# 使用quickstack捕获mysqld的堆栈
/usr/local/xenon/bin/xenoncli perf quickstack  

半同步复制检查

# 开启半同步复制检查
/usr/local/xenon/bin/xenoncli raft enablechecksemisync

# 关闭半同步复制检查
/usr/local/xenon/bin/xenoncli raft disablechecksemisync

参考github:https://github.com/radondb/xenon

本文章仅供参考。

你可能感兴趣的:(mysql,数据库)