Home > 工作记录 > 正文

【工作】MySQL MHA+Atlas读写分离集群搭建详细记录

写在前面:

1.对于MySQL读写分离的实现,很早之前在看一些技术书和文章时候都了解过,但是系统的搭建和研究并没有。而目前公司得数据库架构基本是主主模式,考虑到今后数据访问量的提升,正好最近工作不是太忙,可以空出一些时间,决定仔细研究下MySQL读写分离集群的搭建。

2.本文可以说原创也可以说不是,因为是结合了很多网上的文档和文章编写出来的,而说原创的话,是因为所有的过程都是自己研究并记录的,包括对网上的一些文章中没有解决的问题和一些写的错误的地方都进行了改善和记录。当然本文也有很多不完美的地方,需要改善的地方很多,此文只是记录一下,搭建出一个骨架雏形,真正有血肉的完整体是需要实际应用中不断改进和填补的。

一、架构图

20161227153951

涉及到的主要技术点:

1.MySQL+keepalived主从环境搭建,keepalived设置为非抢占资源模式

2.设置MySQL 5.5的半同步复制,保证数据的一致性,尽可能的减少主库硬件损坏宕机造成的数据丢失,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间(受数据包大小影响),所以,半同步复制最好在低延时的网络中使用,对于数据一致性要求不是很高的业务场景,此步骤可省略。

3.Atlas实现数据库读写分离,读功能的负载均衡,客户端通过Atlas服务器的1234端口访问后端DB服务器

4.MHA实现数据库的高可用,主DB宕掉后提升从DB为主DB,所有从DB指向新的主DB继续提供服务

5.解决发生一次主从切换后,管理节点的Manager服务就会自动退出问题

6.解决原主库DB出问题后,修复后不能自动加入现有MHA集群中问题

此架构的不足之处和改进:

1.MySQL版本有点低,换成5.6或5.7更好一些,MySQL 5.7引入了一种新的半同步方案:Loss-Less半同步复制

2.Atlas读写分离层为单点,需要再增加一台Atlas服务器,结合LVS实现负载均衡和高可用,防止单点故障

3.如果增加LVS,则LVS需要通过keepalived做成主备形式,防止单点故障,可以将LVS和Atlas安装在同一台服务器,节省费用。

4.MHA也为单点,宕掉的影响是如果没有及时发现服务宕掉,当主DB切换到备DB后,所有从DB还是指向原DB,造成数据不同步,目前没有发现有关MHA的高可用方法,个人感觉可以在两台服务器上各安装1个MHA管理节点,一主一备,一个启动服务一个不启动,备机上编写脚本检查主服务器的状态,如果发现主宕机后就启动本机的Manager服务,继续管理MySQL集群。

5.以上问题在搭建线上环境时候需要配置

 

二、搭建环境说明:

网络环境:

20161228143501

软件环境:

20161228143607

三、初始环境安装配置

3.1 配置主机host解析(所有机器)

# vim  /etc/hosts 
172.16.20.227   Master1
172.16.20.226   Master2
172.16.20.225   Slave1
172.16.20.223   Slave2
172.16.20.224   Manager
 

3.2 配置SSH免密码登录(所有机器)

[root@Master1 ~]# ssh-keygen -t rsa
[root@Master1 ~]# ssh-copy-id -i .ssh/id_rsa.pub root@172.16.20.226
[root@Master1 ~]# ssh-copy-id -i .ssh/id_rsa.pub root@172.16.20.225
[root@Master1 ~]# ssh-copy-id -i .ssh/id_rsa.pub root@172.16.20.224
[root@Master1 ~]# ssh-copy-id -i .ssh/id_rsa.pub root@172.16.20.223
 
其他机器除IP地址不同外,操作相同
 

四、MySQL服务安装配置

主库Master(172.16.20.227)操作:

4.1 创建数据库管理帐户:

# useradd -r -s /sbin/nologin mysql

4.2 编译安装MySQL服务:

# yum install -y gcc make cmake ncurses-devel libxml2-devel libtool-devel gcc-c++ autoconf automake bison zlib-devel
# cd /tmp && wget http://dev.mysql.com/get/Downloads/MySQL-5.5/mysql-5.5.42.tar.gz
# tar -zxvf mysql-5.5.42.tar.gz && cd mysql-5.5.42/
# cmake  -DCMAKE_INSTALL_PREFIX=/opt/mysql-5.5.42/   -DMYSQL_DATADIR=/opt/mysql-5.5.42/data   -DMYSQL_UNIX_ADDR=/opt/mysql-5.5.42/data/mysqld.sock   -DWITH_MYISAM_STORAGE_ENGINE=1   -DWITH_INNOBASE_STORAGE_ENGINE=1   -DWITHOUT_PARTITION_STORAGE_ENGINE=1   -DWITH_MEMORY_STORAGE_ENGINE=1   -DWITH_READLINE=1   -DENABLED_LOCAL_INFILE=1   -DMYSQL_USER=mysql  -DMYSQL_TCP_PORT=3306 -DEXTRA_CHARSETS=all -DENABLE_PROFILING=1 -DDEFAULT_CHARSET=utf8 -DDEFAULT_COLLATION=utf8_general_ci -DWITH_DEBUG=0
# make && make install

4.3 配置数据库:

# cp -f support-files/my-innodb-heavy-4G.cnf /etc/my.cnf && cp support-files/mysql.server /etc/init.d/mysqld
# chmod 755 /etc/init.d/mysqld && chkconfig --add mysqld
# echo "export PATH=/opt/mysql-5.5.42/bin/:$PATH" >> /etc/profile && source /etc/profile
# echo "/opt/mysql-5.5.42/mysql/lib/" >> /etc/ld.so.conf && ldconfig
# ln -s /opt/mysql-5.5.42/bin/my*  /usr/bin/

4.4 初始化数据库:

# chown mysql: -R /opt/mysql-5.5.42 && cd /opt/mysql-5.5.42
# ./scripts/mysql_install_db --user=mysql --datadir=/opt/mysql-5.5.42/data/
# /etc/init.d/mysqld start

4.5 提升数据库安全性:

通过MySQL提供的Perl脚本,为root设置密码,移除匿名帐户,禁止root帐户从远程访问,删除测试test数据库,重新加载新的数据库。(第一个选项回车,设置root密码后,其他所有的提问全部选择Y即可)
# /opt/mysql-5.5.42/bin/mysql_secure_installation   

4.6 主库配置文件修改:

# /etc/init.d/mysqld stop
# mkdir /opt/mysql-5.5.42/data/{ibdata,iblogs,bin_log} && chown mysql: -R /opt/mysql-5.5.42
# mv /etc/my.cnf /etc/my.cnf.bak   && vi /etc/my.cnf (添加如下内容进配置文件中)
 
[client]
port            = 3306
socket          = /opt/mysql-5.5.42/data/mysqld.sock
default-character-set=utf8
 
[mysqld]
user=mysql
#skip-slave-start
skip-name-resolve
 
key_buffer_size=2M
read_buffer_size=2M
sort_buffer_size=2M
 
#5.1.3 table_cache deprecated
table_open_cache=16384
 
max_allowed_packet = 16M
max_connect_errors = 3000
max_connections=8000
#net_buffer_length = 8K
#read_rnd_buffer_size = 512K
 
#default-storage-engine=InnoDB
character-set-server=utf8
collation-server=utf8_bin
init-connect='SET NAMES utf8'
binlog_cache_size=2M
 
query_cache_size = 256M 
query_cache_limit = 2M
 
#READ-UNCOMMITTED, READ-COMMITTED, REPEATABLE-READ, SERIALIZABLE
transaction_isolation = REPEATABLE-READ
tmp_table_size=256M
 
auto_increment_offset=1       #ID的初始位置,从设置为2,防止同时写库,主键自增长变量冲突
auto_increment_increment=2    #自增ID的间隔,如1,3,5,间隔为2
 
 
#--------logs---------
slow_query_log=1
slow_query_log_file=slow.log
long_query_time = 2
log-error = err.log
#log_slave_updates=1
 
binlog_format=statement
log-bin=/opt/mysql-5.5.42/data/bin_log/mysql-bin
expire_logs_days = 15
 
#--------innodb--------------
innodb_autoextend_increment = 256
innodb_data_file_path=ibdata1:10M:autoextend
innodb_table_locks = 0
innodb_lock_wait_timeout = 10
innodb_additional_mem_pool_size=128M 
innodb_read_io_threads = 8
innodb_write_io_threads = 4
 
#A recommended value is 2 times the number of CPUs plus the number of disks.
#5.5.8 default 0
#innodb_thread_concurrency = 16
innodb_log_files_in_group = 2
innodb_flush_log_at_trx_commit = 0
innodb_support_xa=0
innodb_file_per_table=1
innodb_buffer_pool_size  = 500M        #根据机器的实际内存调整此参数,(50-80)% * total memory
innodb_log_file_size = 250M                #根据机器的实际内存调整此参数,25% *innodb_buffer_pool_size
innodb_log_buffer_size = 48M
 
#suppression of duplicate-key and no-key-found errors
#slave_exec_mode=IDEMPOTENT
port                            = 3306
socket                          = /opt/mysql-5.5.42/data/mysqld.sock
pid-file                        = /opt/mysql-5.5.42/data/mysql.pid
datadir                         = /opt/mysql-5.5.42/data
innodb_data_home_dir            = /opt/mysql-5.5.42/data/ibdata
innodb_log_group_home_dir       = /opt/mysql-5.5.42/data/iblog
 
server-id                       = 202273306           #两个mysql的server-id不能相同,形式是IP+端口
innodb_file_format              = Barracuda
sync_binlog                     = 3000
innodb_purge_threads            = 1
innodb_purge_batch_size         = 200
innodb_buffer_pool_instances    = 8
innodb_flush_method             = O_DIRECT
 
[mysql]
no-auto-rehash 
 
[mysqldump]
quick
max_allowed_packet = 16M
 
[isamchk]
key_buffer = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M
 
[myisamchk]
key_buffer = 20M
sort_buffer_size = 20M
read_buffer = 2M
write_buffer = 2M
 
[mysqlhotcopy]
interactive-timeout
sync_binlog=1 

4.7 在主库上创建用于复制的账号并在从库上连接测试

 
# mysql -u root -p
mysql> grant replication slave on *.* to 'repl_user'@'172.16.20.%' identified by 'repl_user@ops';
mysql> reset master;
mysql> show master status;      记录二进制日志的位置(后面设置从机的时候会用到) 
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000001 |      107 |              |                  |
+------------------+----------+--------------+------------------+
 

从库Master(172.16.20.226)操作:

4.8 安装配置从Master数据库:软件安装过程同主库

# /etc/init.d/mysqld stop
# mkdir -p /opt/mysql-5.5.42/data/{ibdata,iblogs,bin_log,relay_log} && chown mysql: -R /opt/mysql-5.5.42
# mv /etc/my.cnf /etc/my.cnf.bak   && vi /etc/my.cnf (主从配置文件基本相同,不同的是下面的配置)
 
[mysqld]标签下修改或追加下面的配置
server-id  = 202263306             #修改形式是IP+端口
auto_increment_offset=2         #修改ID的初始位置 
relay_log = /opt/mysql-5.5.42/data/relay_log/mysql-relay-bin   #增加定义relay_log的位置和名称
relay_log_index = /opt/mysql-5.5.42/data/relay_log/mysql-relay-bin.index   #增加定义relay_log的索引文件位置和名称
relay-log-info-file = /opt/mysql-5.5.42/data/relay_log/relay-log.info       
master-info-file  = /opt/mysql-5.5.42/data/bin_log/master.info
 
# /etc/init.d/mysqld start

4.9 从机Slave初始化复制 

# mysql -uroot -p
mysql>change master to master_host='172.16.20.227',master_user='repl_user',master_password='repl_user@ops',master_log_file='mysql-bin.000001', master_log_pos=107; 
mysql> flush privileges;
开启复制:
mysql>slave start ; 
查看复制状态: 
mysql>show slave status\G 
如果出现如下提示,表示主从同步进行中:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
 
测试复制: 
在主机master中对test数据库进行sql操作,再查看从机test数据库是否产生同步。

开启半同步复制:

主从Master上操作相同:
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';
mysql> set global rpl_semi_sync_master_enabled=1;
mysql> set global rpl_semi_sync_master_timeout=1000;
mysql> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
mysql> set global rpl_semi_sync_slave_enabled=1;
 
为了让mysql在重启时自动加载该功能,在主备两个Master的/etc/mysql/my.cnf文件中加入如下参数:
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000
rpl_semi_sync_slave_enabled=1
 
重启从Master上的IO线程,如果没有重启,则默认还是异步复制,重启后,slave会在master上注册为半同步复制的slave角色。
mysql> STOP SLAVE IO_THREAD;
mysql> START SLAVE IO_THREAD;
 
查看半同步复制状态:mysql> show global status like 'Rpl%status%';
 
主Master:

20161228174950

 
备Master:

20161228175120

 
注意:主从开启参数应该相同,因为出现故障切换后,原主DB会自动成为备DB,也需要保持半同步复制状态。

从库slave1(172.16.20.225)操作:

4.10 安装配置从slave1数据库:软件安装过程同主库

# /etc/init.d/mysqld stop
# mkdir /opt/mysql-5.5.42/data/{ibdata,iblogs,bin_log,relay_log} && chown mysql: -R /opt/mysql-5.5.42
# mv /etc/my.cnf /etc/my.cnf.bak   && vi /etc/my.cnf (主从配置文件基本相同,不同的是下面的配置)
 
[mysqld]标签下修改或追加下面的配置
server-id = 202253306          #修改形式是IP+端口
read_only = 1                  #添加只读操作控制
relay_log = /opt/mysql-5.5.42/data/relay_log/mysql-relay-bin               #增加定义relay_log的位置和名称
relay_log_index = /opt/mysql-5.5.42/data/relay_log/mysql-relay-bin.index   #增加定义relay_log的索引文件位置和名称
relay-log-info-file = /opt/mysql-5.5.42/data/relay_log/relay-log.info       
master-info-file  = /opt/mysql-5.5.42/data/bin_log/master.info
 
# /etc/init.d/mysqld start

4.11 从机Slave初始化复制 

# mysql -uroot -p
mysql>change master to master_host='172.16.20.227',master_user='repl_user',master_password='repl_user@ops',master_log_file='mysql-bin.000001', master_log_pos=107; 
mysql> flush privileges;
 
开启复制:
mysql>slave start ; 
查看复制状态: 
mysql>show slave status\G 
如果出现如下提示,表示主从同步进行中:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

从库slave2(172.16.20.223)操作:

4.12 安装配置从slave1数据库:软件安装过程同主库

# /etc/init.d/mysqld stop
# mkdir -p /opt/mysql-5.5.42/data/{ibdata,iblogs,bin_log,relay_log} && chown mysql: -R /opt/mysql-5.5.42
# mv /etc/my.cnf /etc/my.cnf.bak   && vi /etc/my.cnf (主从配置文件基本相同,不同的是下面的配置)
 
[mysqld]标签下修改或追加下面的配置
server-id = 202233306          #修改形式是IP+端口
read_only = 1                  #添加只读操作控制
relay_log = /opt/mysql-5.5.42/data/relay_log/mysql-relay-bin          #增加定义relay_log的位置和名称
relay_log_index = /opt/mysql-5.5.42/data/relay_log/mysql-relay-bin.index   #增加定义relay_log的索引文件位置和名称
relay-log-info-file = /opt/mysql-5.5.42/data/relay_log/relay-log.info       
master-info-file  = /opt/mysql-5.5.42/data/bin_log/master.info
 
# /etc/init.d/mysqld start

4.13 从机Slave初始化复制 

# mysql -uroot -p
mysql>change master to master_host='172.16.20.227',master_user='repl_user',master_password='repl_user@ops',master_log_file='mysql-bin.000001', master_log_pos=107; 
mysql> flush privileges;
 
开启复制:
mysql>slave start ; 
查看复制状态: 
mysql>show slave status\G 
如果出现如下提示,表示主从同步进行中:
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
 

五、MHA安装配置

MHA运行的基本原理:

1、从宕机崩溃的master保存二进制日志事件(binlog events)
2、识别含有最新更新的slave
3、应用差异的中继日志(relay log)到其他的slave
4、应用从master保存的二进制日志事件(binlog events)
5、提升一个slave为新的master
6、使其他的slave连接新的master进行复制,全部完成后再变更为新的主从关系
 
在MySQL故障切换过程中,MHA能做到在 0~30秒之内自动完成数据库的故障切换操作,并且在进行故障切换的过程中,MHA能在最大程度上保证数据的一致性,以达到真正意义上的高可用。
 

MHA软件由两部分组成,Manager工具包和Node工具包,具体的说明如下:

Manager工具包主要包括以下几个工具:
  masterha_check_ssh      检查 MHA 的 SSH 配置状况
  masterha_check_repl     检查 MySQL 复制状况
  masterha_manger         启动 MHA
  masterha_check_status   测当前 MHA 运行状态
  masterha_master_monitor 检测 master 是否宕机
  masterha_master_switch  控制故障转移(自动或者手动)
  masterha_conf_host      添加或删除配置的 server 信息
 
Node工具包(这些工具通常由MHA Manager的脚本触发,无需人为操作)主要包括以下几个工具:
  save_binary_logs 保存和复制 master 的二进制日志
  apply_diff_relay_logs 识别差异的中继日志事件并将其差异的事件应用于其它slave        
  purge_relay_logs 清除中继日志(不会阻塞 SQL 线程)
  
mha-manager常用命令:
  开启MHA Manager监控
  1、nohup masterha_manager --conf=/etc/masterha/app1.cnf --ignore_last_failover < /dev/null > /etc/masterha/app1/app1.log2>&1 &   
  查看MHA Manager监控是否正常
  2、masterha_check_status --conf=/etc/masterha/app1.cnf 
  关闭MHA Manage监控
  3、masterha_stop --conf=/etc/masterha/app1.cnf 
  检查SSH连接配置是否正常
  4、masterha_check_ssh --conf=/etc/masterha/app1.cnf   
  检查整个复制环境状况是否正常
  5、masterha_check_repl --conf=/etc/masterha/app1.cnf  
 
MHA软件下载地址(貌似失效无法下载): https://code.google.com/p/mysql-master-ha/downloads/list
CSDN下载地址:http://download.csdn.net/detail/blog_liuliang/9695450

5.1 所有Note节点安装

# yum install -y perl-DBD-MySQL  
# rpm -ivh mha4mysql-node-0.54-0.el6.noarch.rpm  

5.2 Manager节点安装:

默认yum是无法安装perl-Log-Dispatch perl-Parallel-ForkManager这个两个依赖的,需要安装新的yum源
# wget http://ftp.tu-chemnitz.de/pub/linux/dag/redhat/el6/en/i386/rpmforge/RPMS/rpmforge-release-0.5.3-1.el6.rf.i686.rpm
# rpm -ivh rpmforge-release-0.5.3-1.el6.rf.i686.rpm
# yum install -y perl-DBD-MySQL perl-Config-Tiny perl-Time-HiRes  perl-Log-Dispatch perl-Parallel-ForkManager  
# rpm -ivh mha4mysql-manager-0.55-0.el6.noarch.rpm  
 
注意:manager和node节点的版本可以不一样,Note节点和Manager节点安装完成后会在/usr/bin/目录下生成一些脚本文件

5.3 配置mha manager

1,添加管理账号,所有数据库都执行以下操作:
mysql> grant all privileges on *.* TO mha_manager@'172.16.%.%' IDENTIFIED BY 'Manager_MHA@20161202';  
mysql>  flush privileges;  
 
2. 创建MHA的工作目录,并且创建相关配置文件
# mkdir -p /etc/masterha/app1
# vi /etc/masterha/app1.cnf  
[server default]
manager_workdir=/etc/masterha/app1                      #manager工作目录       
manager_log=/etc/masterha/app1/app1.log                  #管理节点工作日志文件
 
password=Manager_MHA@20161202         #设置mysql中管理用户的密码,这个密码是前文中创建mha_manager的密码
user=mha_manager                      #设置监控用户
ping_interval=1     #设置监控主库发送ping包的时间间隔,默认是3秒,尝试三次没有回应的时候自动进行railover
remote_workdir=/etc/masterha/app1     #设置远端mysql在发生切换时binlog的保存位置
repl_password=repl_user@ops           #设置复制用户的密码
repl_user=repl_user                   #设置复制环境中的复制用户名
ssh_user=root                        #设置ssh的登录用户名
 
[server1]
candidate_master=1
check_repl_delay=0
hostname=172.16.20.227
port=3306
master_binlog_dir=/opt/mysql-5.5.42/data/bin_log/
 
 
[server2]
hostname=172.16.20.226
port=3306
candidate_master=1
check_repl_delay=0  
master_binlog_dir=/opt/mysql-5.5.42/data/bin_log/               
 
[server3]
hostname=172.16.20.225
port=3306
master_binlog_dir=/opt/mysql-5.5.42/data/bin_log/
no_master=1
 
[server4]
hostname=172.16.20.223
port=3306
master_binlog_dir=/opt/mysql-5.5.42/data/bin_log/
no_master=1
 
参数解释:
candidate_master=1  
#设置为候选master,如果设置该参数以后,发生主从切换以后将会将此从库提升为主库,即使这个主库不是集群中事件最新的slave
check_repl_delay=0   
#默认情况下如果一个slave落后master 100M的relay logs的话,MHA将不会选择该slave作为一个新的master,因为对于这个slave的恢复需要花费很长时间,通过设置check_repl_delay=0,MHA触发切换在选择一个新的master的时候将会忽略复制延时,这个参数对于设置了candidate_master=1的主机非常有用,因为这个候选主在切换的过程中一定是新的master
master_binlog_dir=  
#设置DB服务器上保存binlog的位置,以便MHA可以找到日志文件
 
3.所有Note节点设置
增加remote_workdir(运行MySQL实例的服务器)的工作目录权限,其中会生成日志文件
# mkdir -p /etc/masterha/app1
 
4.通过masterha_check_ssh和masterha_check_repl检查没有问题
[root@Manager opt]# /usr/bin/masterha_check_ssh --conf=/etc/masterha/app1.cnf 
#最后出现如下提示表示没有问题
Mon Dec 19 16:42:38 2016 - [info] All SSH connection tests passed successfully.
 
[root@Manager opt]# /usr/bin/masterha_check_repl --conf=/etc/masterha/app1.cnf  
#最后出现如下提示表示没有问题
MySQL Replication Health is OK.
 
注意:如果执行此命令报如下错误,解决办法如下:
报错:Can't exec "mysqlbinlog": No such file or directory at /usr/local/perl5/MHA/BinlogManager.pm line 99.
解决:# ln -s /opt/mysql-5.5.42/bin/my*  /usr/bin/
 
5.启动mha-manager服务
# nohup masterha_manager --conf=/etc/masterha/app1.cnf --ignore_last_failover --ignore_last_failover &
 
常用参数:
--remove_dead_master_conf    
#该参数代表当发生主从切换后,老的主库的ip将会从配置文件中移除。
--ignore_fail_on_start        
#当有slave节点宕掉的情况是启动不了的,加上--ignore_fail_on_start即使有节点宕掉也能启动mha
--manger_log=/etc/masterha/app1/app1.log  
#日志存放位置
--ignore_last_failover                 
#在缺省情况下,如果MHA检测到连续发生宕机,且两次宕机间隔不足8小时的话,则不会进行Failover,之所以这样限制是为了避免ping-pong效应。该参数代表忽略上次MHA触发切换产生的文件,默认情况下,MHA发生切换后会在日志目录,也就是上面我设置的/data产生app1.failover.complete文件,下次再次切换的时候如果
发现该目录下存在该文件将不允许触发切换,除非在第一次切换后收到删除该文件,为了方便,这里设置为--ignore_last_failover。
 
通过master_check_status脚本查看Manager的状态:
# masterha_check_status --conf=/etc/masterha/app1.cnf
app1 (pid:50176) is running(0:PING_OK), master:172.16.20.227
 
以上MHA就配置完成了。
 

六、安装配置keepalived服务

6.1 防火墙设置:

如果非要开启iptables的话,需要作些设定
iptables -I INPUT -i eth0 -d 224.0.0.0/8 -j ACCEPT 
service iptables save
 
keepalived使用224.0.0.18作为Master和Backup健康检查的组播地址,eth0是下面的通信地址IP

6.2 安装keepalived服务(主备master安装方法相同)

# yum install openssl-devel kernel-devel -y
# cd /tmp && wget http://www.keepalived.org/software/keepalived-1.2.19.tar.gz
# tar zxvf keepalived-1.2.19.tar.gz && cd keepalived-1.2.19
# ./configure --prefix=/opt/keepalived
# make && make install

6.3 keepalived服务配置:

# cp /opt/keepalived/sbin/keepalived /usr/sbin/ && cp /opt/keepalived/etc/rc.d/init.d/keepalived /etc/init.d/ && cp /opt/keepalived/etc/sysconfig/keepalived /etc/sysconfig/
# chmod +x /etc/init.d/keepalived && chkconfig --add keepalived
# vim /etc/sysconfig/keepalived (修改KEEPALIVED_OPTIONS参数如下)
KEEPALIVED_OPTIONS="-f /opt/keepalived/etc/keepalived/keepalived.conf -D -P -S 4"
 
# cp /opt/keepalived/etc/keepalived/keepalived.conf{,.bak}  && echo > /opt/keepalived/etc/keepalived/keepalived.conf
# vim /opt/keepalived/etc/keepalived/keepalived.conf  (主master配置文件)
 
注意:
1.router_id和vrrp_instance以及virtual_router_id这三个参数后的值要保持整个网段内唯一,最好设置为vip,否则会造成其他网段无法ping通的情况出现
2.主DB的keepalived要设置成非抢占资源模式,实现方式就是添加nopreempt参数,并把“state MASTER”改成“state BACKUP”
 
! Configuration File for keepalived
 
global_defs {
   router_id LVS_DEVEL
}
 
vrrp_script chk_mysql {
    script "/bin/bash /opt/keepalived/sbin/mysql.sh"
    interval 2                                              
    weight -20
}
 
vrrp_instance HA_1 {
    state BACKUP
    nopreempt             #设置 nopreempt 防止抢占资源  
    interface eth0              
    virtual_router_id 80        
    priority 90               
    advert_int 2               
    authentication {
        auth_type PASS
        auth_pass 1111
    }
 
    virtual_ipaddress {                
        172.16.20.228/24 dev eth0     
    }
}
 
track_script {
       chk_mysql
   }
 
# vim /opt/keepalived/etc/keepalived/keepalived.conf  (备master配置文件)
! Configuration File for keepalived
 
global_defs {
   router_id LVS_DEVEL
}
 
vrrp_script chk_mysql {                                      
    script "/bin/bash /opt/keepalived/sbin/mysql.sh"
    interval 2
    weight -20                                          
}
 
vrrp_instance HA_1 {
    state BACKUP        
    interface eth0              
    virtual_router_id 80        
    priority 90                
    advert_int 2       
    authentication {
        auth_type PASS
        auth_pass 1111
    }
 
    virtual_ipaddress {                
        172.16.20.228/24 dev eth0       
    }
track_script {
       chk_mysql
   }
 
# vim /opt/keepalived/sbin/mysql.sh  (mysql检查脚本)
#!/bin/bash
 
PATH=/bin:/usr/bin:/sbin:/usr/sbin; export PATH
mysql_id=`ps -C mysqld --noheader |wc -l`
 
if [ $mysql_id -eq 0 ];then
/etc/init.d/keepalived stop
fi
 
给所有的脚本执行权限
# chmod +x /opt/keepalived/sbin/*.sh   

6.3.主备启动,查看vip状况

# /etc/init.d/keepalived start
 

七、故障测试和优化:

7.1 测试主备切换

手工停止主Master(172.16.20.227)上的mysql服务,大约30秒,备master(172.16.20.226)自动成为新的主库,在slave从库(172.16.20.225)上查看从库状态:
20161220104217
 
可以看出此时主库已经变成备master(172.16.20.226),到此基本上MHA功能已经实现,但是还是有一些问题需要优化。
 

7.2 最后优化

配置完成后还有如下2个问题需要解决:
 
1.当主Master故障切换到备Master后,即使启动了原来的主Master服务,也不能自动加入到整个MHA系统中,需要在主Master上重新执行命令,手动加入主从架构中,手动加入的方法如下:
 
管理节点上:
# grep -i "All other slaves should start" /etc/masterha/app1/app1.log
Mon Dec 19 18:23:07 2016 - [info]  All other slaves should start replication from here. Statement should be: CHANGE MASTER TO MASTER_HOST='172.16.20.226', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=591, MASTER_USER='repl_user', MASTER_PASSWORD='xxx';
 
将上面的命令在主master上执行一下,然后开启从库,命令如下(需要更改xxx为同步密码):
mysql> CHANGE MASTER TO MASTER_HOST='172.16.20.226', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=591, MASTER_USER='repl_user', MASTER_PASSWORD='repl_user@ops';
mysql> slave start;
 
2.发生一次主从切换后,管理节点的manager服务就会自动退出,需要用脚本来自动启动。另外在MHA的工作目录/etc/masterha/app1下会生成app1.failover.complete这个文件,需要删除,否则如果新生成的主Master出现问题后,MHA就不会再次切换,但是如果启动参数设置为--ignore_last_failover,即使生成也不会影响切换。
 

7.3 修复当主从故障切换一次后,mha-manager服务会自动退出

# vi /etc/masterha/app1/monitor.sh   #增加如下内容
#!/bin/bash
while :
do
#获取关于mha-manager的进程
mhapid=`ps -ef|grep -v grep|grep masterha_manager |wc -l`                          
echo "mhapid:" $mhapid
if [ $mhapid -eq 0 ];then
/usr/bin/nohup masterha_manager --conf=/etc/masterha/app1.cnf --ignore_fail_on_start --ignore_last_failover &
#调用另一个脚本,脚本见下面7.4
/etc/masterha/app1/check_old_master.sh
fi
sleep 5
done
 
注:测试发现,脚本里采用“nohup masterha_manager --conf=/etc/masterha/app1.cnf --ignore_last_failover < /dev/null > /app/masterha/app1/app1.log2>&1 &”命令的方式启动的话,每次log日志都会被清空 ,这样下面的脚本中获取log内容就会失败,导致无法修复主从问题,从而造成MHA监控服务也无法正常启动
 

7.4 修复原主库出问题,恢复后不能自动加入现有MHA集群中

1、在mha manager节点上创建脚本
(1) vi /etc/masterha/app1/check_old_master.sh   #增加如下内容
#!/bin/bash
 
#从mha manager日志中提取关于加入到新AB架构的信息,存放到cmd.txt中
`awk -F: '/All other slaves should start replication from here. Statement should be:/{a=$4}END{print a}'  /etc/masterha/app1/app1.log |sed  "s/xxx/repl_user@ops/" > /etc/masterha/app1/cmd.txt`
 
#声明主从库的IP
master1="172.16.20.227"    
master2="172.16.20.226"
 
#获取新主库的IP,如果当前主为主库1,将提取的信息scop到主库2,如果当前主为主库2,将提取的信息scop到主库1
new_master=$(awk '/as a new master./{a=$2}END{print a}' /etc/masterha/app1/app1.log)   
echo 'the new master: '${new_master}
if [ "$master1" = "$new_master" ]    
then
    echo "the text scop begin :"${master2}
    scp /etc/masterha/app1/cmd.txt 172.16.20.226:/etc/masterha/app1
else
    echo "the text scop begin :"${master1}  
    scp /etc/masterha/app1/cmd.txt 172.16.20.227:/etc/masterha/app1
fi
 
2、在主备Master上创建脚本
(1) vi /etc/masterha/app1/add_ab.sh   #增加如下内容
 
#!/bin/bash
MYSQL=/opt/mysql-5.5.42/bin/mysql     #声明Mysql命令绝对路径
MYSQL_HOST=localhost                 #声明IP
MYSQL_USER=root                     #声明登录mysql的用户
MYSQL_PASSWORD=123123                     #声明登录mysql的用户的密码
MyFile="/etc/masterha/app1/cmd.txt"           #指定读取文件的绝对路径
Content=$(cat /etc/masterha/app1/cmd.txt)   #Content保存cmd文件的内容
echo "Content:" $Content
if [ ! -f "$MyFile" ]
then 
   echo "the text is not exist, Don't do anything!!!!"   
else
   echo "the text is exist,add the AB!!!!"  
   $MYSQL -h $MYSQL_HOST -u $MYSQL_USER -p${MYSQL_PASSWORD} -e "stop slave;"       #停止AB复制
   $MYSQL -h $MYSQL_HOST -u $MYSQL_USER -p${MYSQL_PASSWORD} -e "$Content;"         #加入现有AB架构
   $MYSQL -h $MYSQL_HOST -u $MYSQL_USER -p${MYSQL_PASSWORD} -e "start slave;"      #开始AB复制
   rm -rf $MyFile 
fi 
 
(3) vi /etc/masterha/app1/monitor.sh   #增加如下内容
#!/bin/bash
while : 
do
sh /etc/masterha/app1/add_ab.sh 
sleep 5
done
 
MHA manager上操作:
# chmod +x /etc/masterha/app1/{check_old_master.sh,monitor.sh} 
# nohup sh /etc/masterha/app1/monitor.sh &
 
主备Master上操作:
# chmod +x /etc/masterha/app1/{add_ab.sh,monitor.sh} 
# nohup sh /etc/masterha/app1/monitor.sh &  
 

7.5 设置relay log的清除:

MHA在发生切换的过程中,从库的恢复过程中依赖于relay log的相关信息,所以这里要将relay log的自动清除设置为OFF,采用手动清除relay log的方式。在默认情况下,从服务器上的中继日志会在SQL线程执行完毕后被自动删除。
 
但是在MHA环境中,这些中继日志在恢复其他从服务器时可能会被用到,因此需要禁用中继日志的自动删除功能。定期清除中继日志需要考虑到复制延时的问题。在ext3的文件系统下,删除大的文件需要一定的时间,会导致严重的复制延时。为了避免复制延时,需要暂时为中继日志创建硬链接,因为在linux系统中通过硬链接删除大文件速度会很快。(在mysql数据库中,删除大表时,通常也采用建立硬链接的方式)
 
MHA节点中包含了pure_relay_logs命令工具,它可以为中继日志创建硬链接,执行SET GLOBAL relay_log_purge=1,等待几秒钟以便SQL线程切换到新的中继日志,再执行SET GLOBAL relay_log_purge=0。
 
pure_relay_logs脚本参数如下所示:
--user mysql                    #用户名
--password mysql                #密码
--port                          #端口号
--workdir                    
#指定创建relay log的硬链接的位置,默认是/var/tmp,由于系统不同分区创建硬链接文件会失败,故需要执行硬链接具体位置,成功执行脚本后,硬链接的中继日志文件被删除
--disable_relay_log_purge    
#默认情况下,如果relay_log_purge=1(ON),脚本会什么都不清理,自动退出,通过设定这个参数,当relay_log_purge=1的情况下会将relay_log_purge设置0。清理relay lo之后,最后将参数设置为OFF。
 
下面我们开始操作一下
(1) 设置relay log的清除方式(在每个slave节点上)
# mysql -u root -p123123 -e 'set global relay_log_purge=0'
 
(2) 设置定期清理relay脚本(在每个slave节点上)
# vi /etc/masterha/app1/purge_relay_log.sh 
#!/bin/bash
user=root        
passwd=123123     
port=3306        
work_dir=/etc/masterha/app1
purge=/usr/bin/purge_relay_logs 
if [ ! -d $work_dir ]   
    then
   mkdir $work_dir -p  
fi
$purge --user=$user --password=$passwd --disable_relay_log_purge --port=$port --workdir=$work_dir >> $work_dir/purge_relay_logs.log 2>&1
 
(3)添加到crontab定期执行计划任务
# chmod 775 /etc/masterha/app1/purge_relay_log.sh
# crontab -e
0 4 * * * /bin/bash /etc/masterha/app1/purge_relay_log.sh 
 
(4) purge_relay_logs脚本删除中继日志不会阻塞SQL线程,下面在备Master上手动执行
# /usr/bin/purge_relay_logs --user=root --password=123123 --disable_relay_log_purge --port=3306 --workdir=/etc/masterha/app1 >> /etc/masterha/app1/purge_relay_logs.log
# cat purge_relay_logs.log
2016-12-23 18:18:20: purge_relay_logs script started.
 Found relay_log.info: /opt/mysql-5.5.42/data/relay_log/relay-log.info
 Removing hard linked relay log files relay-bin* under /etc/masterha/app1.. done.
 Current relay log file: /opt/mysql-5.5.42/data/relay_log/relay-bin.000002
 Archiving unused relay log files (up to /opt/mysql-5.5.42/data/relay_log/relay-bin.000001) ...
 Creating hard link for /opt/mysql-5.5.42/data/relay_log/relay-bin.000001 under /etc/masterha/app1/relay-bin.000001 .. ok.
 Creating hard links for unused relay log files completed.
 Executing SET GLOBAL relay_log_purge=1; FLUSH LOGS; sleeping a few seconds so that SQL thread can delete older relay log files (if it keeps up); SET GLOBAL relay_log_purge=0; .. ok.
 Removing hard linked relay log files relay-bin* under /etc/masterha/app1.. done.
2016-12-23 18:18:23: All relay log purging operations succeeded.
 
 出现上面日志代表配置成功!

八、Atlas读写分离配置

常见实现读写分离的方法:

  a.开发在代码中写死,指定修改操作,连接的是主库。查询操作,连接的是从库
  b.采用中间件实现,比如TDDL、Cobar、Atlas、MyCat、Mysql Proxy、Oceanus等,中间件的最大的好处是对数据层解耦,减轻了运维难度,当然在服务器层与数据库层加了一层proxy,使得效率明显不如直接访问数据库
 
我们这套架构中的读写分离是使用Atlas实现的,Atlas是由 Qihoo 360,  Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目。它在MySQL官方推出的MySQL-Proxy0.8.2版本的基础上,修改了大量bug,添加了很多功能特性。目前该项目在360公司内部得到了广泛应用,很多MySQL业务已经接入了Atlas平台,每天承载的读写请求数达几十亿条。

主要功能:

  a.读写分离
  b.从库负载均衡
  c.IP过滤
  d.SQL语句黑白名单
  e.自动分表

8.1 主库和所有从库全操作

mysql> GRANT ALL PRIVILEGES ON *.* TO 'Atlaser'@'%' identified by 'mysql@123';
mysql> FLUSH PRIVILEGES;

8.2 安装Atlas服务器(172.16.20.224)

项目地址:https://github.com/Qihoo360/Atlas/releases
下载版本:Atlas-2.2.1.el6.x86_64.rpm
# rpm -ivh Atlas-2.2.1.el6.x86_64.rpm      #rpm方式安装
# rpm -ql Atlas                      #查看Atlas安装的详细路径

8.3 修改Atlas配置文件

# cd /usr/local/mysql-proxy/conf  &&  mv test.cnf test.cnf.old
# grep -v '^#' /usr/local/mysql-proxy/conf/test.cnf.old  | grep -v '^$'  > /usr/local/mysql-proxy/conf/test.cnf
# /usr/local/mysql-proxy/bin/encrypt mysql@123    #加密上面创建的Atlaser用户密码
L8aSVWv2C9+TZ6zxvGQr9A==
 
# vim /usr/local/mysql-proxy/conf/test.cnf 
[mysql-proxy]
admin-username = admin
admin-password = P@ssw0rd123
proxy-backend-addresses = 172.16.20.228:3306
proxy-read-only-backend-addresses = 172.16.20.225:3306,172.16.20.223:3306
pwds = Atlaser:L8aSVWv2C9+TZ6zxvGQr9A==
daemon = true
keepalive = true
event-threads = 4
sql-log = ON
sql-log-slow = 5
log-level = message
log-path = /usr/local/mysql-proxy/log
instance = test
charset = utf8
proxy-address = 0.0.0.0:1234
admin-address = 0.0.0.0:2345
 
注意:关于各项参数的意思,在test.cnf.old文件中都有详细解释。
 
启动Atlas,查看监听端口:
# netstat -tanlp | grep mysql
# /usr/local/mysql-proxy/bin/mysql-proxyd test start    #test必须跟配置文件的名字相同,以此区分不同集群
# netstat -tanlp | grep mysql
tcp        0      0 0.0.0.0:2345                0.0.0.0:*                   LISTEN      34058/mysql-proxy   
tcp        0      0 0.0.0.0:1234                0.0.0.0:*                   LISTEN      34058/mysql-proxy   
 
登录并管理Atlas:
# mysql -h 172.16.20.224 -P 2345 -u admin -pP@ssw0rd123   #登录管理
mysql> select * from help;                                                                   #查看Atlas帮助
+-------------------+---------------------------------------+
| command                  | description                                               |
+-------------------+---------------------------------------+
| SELECT * FROM help         | shows this help                                         |
| SELECT * FROM backends     | lists the backends and their state                      |
| SET OFFLINE $backend_id    | offline backend server, $backend_id is backend_ndx's id |
| SET ONLINE $backend_id     | online backend server, ...                              |
| ADD MASTER $backend        | example: "add master 127.0.0.1:3306", ...               |
| ADD SLAVE $backend         | example: "add slave 127.0.0.1:3306", ...                |
| REMOVE BACKEND $backend_id | example: "remove backend 1", ...                        |
| ADD CLIENT $client         | example: "add client 192.168.1.2", ...                  |
| REMOVE CLIENT $client      | example: "remove client 192.168.1.2", ...               |
| SAVE CONFIG                | save the backends to config file                        |
+-------------------+---------------------------------------+
10 rows in set (0.00 sec)
 
mysql> SELECT * FROM backends;      #查看DB状态
+-------------+--------------------+-------+------+
| backend_ndx | address            | state | type |
+-------------+--------------------+-------+------+
|           1 | 172.16.20.228:3306 | up    | rw   |
|           2 | 172.16.20.225:3306 | up    | ro   |
|           3 | 172.16.20.223:3306 | up    | ro   |
+-------------+--------------------+-------+------+
3 rows in set (0.00 sec)
 
正常通过代理操作:
# mysql -h 172.16.20.224 -P 1234 -uAtlaser -pmysql@123    #通过代理机器登录mysql
mysql>  SHOW VARIABLES LIKE 'server_id';                        #查看当前Mysql的序号
+---------------+-----------+
| Variable_name | Value     |
+---------------+-----------+
| server_id     | 202253306 |
+---------------+-----------+
1 row in set (0.00 sec)
mysql>  SHOW VARIABLES LIKE 'server_id';
+---------------+-----------+
| Variable_name | Value     |
+---------------+-----------+
| server_id     | 202233306 |
+---------------+-----------+
1 row in set (0.00 sec)
 
注:发现上面的SHOW VARIABLES LIKE 'server_id';操作后每次的结果都不一样,分别是2台从库的server_id,并且每执行一次命令,server_id就会变换一次,这是因为默认读操作的权重都是1,两台从DB默认就是负载均衡,至此我们的读写分离功能配置成功了!

九、报错解决:

1.问题:主从切换一次后,MHA服务无法正常启动,报错如下:
Wed Dec 21 16:16:09 2016 - [error][/usr/share/perl5/vendor_perl/MHA/ServerManager.pm, ln604] There are 2 non-slave servers! MHA manages at most one non-slave server. Check configurations.
Wed Dec 21 16:16:09 2016 - [error][/usr/share/perl5/vendor_perl/MHA/MasterMonitor.pm, ln386] Error happend on checking configurations.  at /usr/share/perl5/vendor_perl/MHA/MasterMonitor.pm line 300
Wed Dec 21 16:16:09 2016 - [error][/usr/share/perl5/vendor_perl/MHA/MasterMonitor.pm, ln482] Error happened on monitoring servers.
Wed Dec 21 16:16:09 2016 - [info] Got exit code 1 (Not master dead).
 
解决方法:
是因为主从切换后,原来的主Matser并没有变成从Matser,而MHA记录的新Master还是备机,所以现在有两个备机“2 non-slave servers”,解决办法是查看现在主Matser信息,然后手动启动slave同步:
mysql> CHANGE MASTER TO MASTER_HOST='172.16.20.227', MASTER_PORT=3306, MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=107, MASTER_USER='repl_user', MASTER_PASSWORD='repl_user@ops';
mysql> slave start;
 
2.执行/usr/bin/masterha_check_repl --conf=/etc/masterha/app1.cnf  此命令报如下错误:

Can't exec "mysqlbinlog": No such file or directory at /usr/local/perl5/MHA/BinlogManager.pm line 99.
 
解决办法:
所有DB服务器上执行如下命令
# ln -s /opt/mysql-5.5.42/bin/my*  /usr/bin/
上一篇:【案例】唯品会NoSQL平台自动化发展及运维经验分享
下一篇:【理论】关于TCP/IP传输层的知识点
【工作】生产环境中MySQL Drop 删除(百G级、T级)大表的解决方法

【工作】生产环境中MySQL Drop 删除(百G级、T级)大表的解决方法

【分享】100+个MySQL调试和优化技巧

【分享】100+个MySQL调试和优化技巧

【分享】10个最经典的 MySQL 数据库运维实战案例

【分享】10个最经典的 MySQL 数据库运维实战案例

【案例】云智慧:如何进行PB级别数据的架构变迁

【案例】云智慧:如何进行PB级别数据的架构变迁

【分享】线上环境到底要不要开启query cache

【分享】线上环境到底要不要开启query cache

【案例】nice公司服务端架构重构与演进

【案例】nice公司服务端架构重构与演进

【工作】解决程序远程连接mysql 5.7.17 执行select查询报错

【工作】解决程序远程连接mysql 5.7.17 执行select查询报错”Got an error writing communication packets”

发表评论

昵称 *
邮箱 *
网址

沙发空闲中,快来抢!