准备工作
1.主从数据库版本最好一致
2.主从数据库内数据保持一致
主数据库:121.199.27.227 /ubuntu 16.04 MySQL 5.7.21 (阿里云)
从数据库:182.254.149.39 /ubuntu 16.04 MySQL 5.7.21 (腾讯云)
防火墙配置
配置主服务器只允许特定IP访问数据库的端口,避免不必要的攻击。
主库防火墙配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
# iptables -A INPUT -p tcp -s slave_ip --dport 3306 -j ACCEPT
#删除可能已经存在的配置,避免出现多条重复记录
$ sudo iptables -D INPUT -p tcp -s 182.254.149.39 --dport 3306 -j ACCEPT
$ sudo iptables -D INPUT -p tcp -s 127.0.0.1 --dport 3306 -j ACCEPT
$ sudo iptables -D INPUT -p tcp --dport 3306 -j DROP
$ sudo iptables -D INPUT -p udp --dport 3306 -j DROP
$ sudo iptables -D INPUT -p sctp --dport 3306 -j DROP
#增加配置,只允许特定地址访问数据库端口
$ sudo iptables -A INPUT -p tcp -s 182.254.149.39 --dport 3306 -j ACCEPT
$ sudo iptables -A INPUT -p tcp -s 127.0.0.1 --dport 3306 -j ACCEPT
$ sudo iptables -A INPUT -p tcp --dport 3306 -j DROP
$ sudo iptables -A INPUT -p udp --dport 3306 -j DROP
$ sudo iptables -A INPUT -p sctp --dport 3306 -j DROP
$ sudo iptables -L -n
#保存配置
$ sudo apt-get install iptables-persistent
$ sudo netfilter-persistent save
#配置被保存到/etc/iptables/rules.v4 /etc/iptables/rules.v6这两个文件下面,
#最好确认一下实际保存的内容,尤其是安装了denyhosts等其他安全软件的情况下,
#可能会记录了多余的规则,需要手工删除
|
从库防火墙配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
# iptables -A OUTPUT -p tcp -d master_ip --dport 3306 -j ACCEPT
#删除可能已经存在的配置,避免出现多条重复记录
$ sudo iptables -D OUTPUT -p tcp -d 121.199.27.227 --dport 3306 -j ACCEPT
#增加配置
$ sudo iptables -A OUTPUT -p tcp -d 121.199.27.227 --dport 3306 -j ACCEPT
$ sudo iptables -L -n
#保存配置
$ sudo apt-get install iptables-persistent
$ sudo netfilter-persistent save
#配置被保存到/etc/iptables/rules.v4 /etc/iptables/rules.v6这两个文件下面,
#最好确认一下实际保存的内容,尤其是安装了denyhosts等其他安全软件的情况下,
#可能会记录了多余的规则,需要手工删除
|
主数据库master配置
1.修改mysql配置
1
|
$ sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
|
在[mysqld]部分进行如下修改:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
[mysqld]
#开启二进制日志,默认是注释掉的,我们去掉注释
log-bin = /var/log/mysql/mysql-bin.log
#设置server-id
server-id = 1
#默认是127.0.0.1,此处我们设置为任意地址,放开远程访问,这么操作之前一定要确保防火墙配置正确,否则会产生安全风险
bind-address = 0.0.0.0
#如果数据库是从5.7版本之前升级的,并且是wordpress那么会遇到无法更改数据库的情况,
#NO_ZERO_IN_DATE,NO_ZERO_DATE这两个参数限制的,我们需要去掉这个限制,原因在于
#wordpress创建的表中存在
#`comment_date` datetime(0) NOT NULL DEFAULT '0000-00-00 00:00:00',
#这样的定义是没办法进行后续的操作的,因此我们需要重新定义sql_mode来解除这个限制
sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
|
2.修改需要同步的表的引擎为INNODB,只有INNODB支持主从,MyISAM不支持
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 此处以wordpress为例,默认情况下wordpress中的wp_options表为MyISAM引擎,
# 这会导致数据同步失败,可能出现的错误信息如下:
# Last_Errno: 1032
# Last_Error: Could not execute Delete_rows event on table wordpress.wp_options; Can't find record in 'wp_options', Error_code: 1032; handler error HA_ERR_KEY_NOT_FOUND; the event's master log mysql-bin.000007, end_log_pos 3678486
# 使用“show create table wp_options\G;”查看表的引擎信息
# 单个表修改的命令如下
#mysql -u root -p -e "use wordpress; ALTER TABLE wp_options ENGINE=INNODB;"
#整个数据库修改的命令如下,下面的语句只是生成执行语句,具体执行,还需要把结果拷贝出来执行
$ mysql -u root -p -e "USE wordpress; SET @DATABASE_NAME = 'wordpress'; SELECT GROUP_CONCAT(CONCAT( 'ALTER TABLE ' ,TABLE_NAME ,' ENGINE=InnoDB; ') SEPARATOR '' ) FROM information_schema.TABLES AS t WHERE TABLE_SCHEMA = @DATABASE_NAME AND TABLE_TYPE = 'BASE TABLE' AND ENGINE = 'MyISAM';"
#我这边的例子如下
$ mysql -u root -p -e "USE wordpress; ALTER TABLE wp_IPBLC_blacklist ENGINE=InnoDB; ALTER TABLE wp_IPBLC_login_failed ENGINE=InnoDB; ALTER TABLE wp_IPBLC_usernames ENGINE=InnoDB; ALTER TABLE wp_commentmeta ENGINE=InnoDB; ALTER TABLE wp_comments ENGINE=InnoDB; ALTER TABLE wp_links ENGINE=InnoDB; ALTER TABLE wp_options ENGINE=InnoDB; ALTER TABLE wp_postmeta ENGINE=InnoDB; ALTER TABLE wp_posts ENGINE=InnoDB; ALTER TABLE wp_term_relationships ENGINE=InnoDB; ALTER TABLE wp_term_taxonomy ENGINE=InnoDB; ALTER TABLE wp_terms ENGINE=InnoDB; ALTER TABLE wp_usermeta ENGINE=InnoDB; ALTER TABLE wp_users ENGINE=InnoDB;"
|
3.重启mysql,创建用于同步的用户账号
创建用户并授权:用户:repl 密码:slavepass
1
2
3
4
5
6
7
8
|
$ sudo service mysql restart
$ mysql -u root -p -e "CREATE USER 'repl'@'182.254.149.39' IDENTIFIED BY 'slavepass';" #创建用户
$ mysql -u root -p -e "GRANT REPLICATION SLAVE ON *.* TO 'repl'@'182.254.149.39';" #分配权限
$ mysql -u root -p -e "flush privileges;" #刷新权限
|
4.查看master状态,记录二进制文件名(mysql-bin.000001)和位置(333802):
1
2
3
4
5
6
7
|
$ mysql -u root -p -e "SHOW MASTER STATUS;"
Enter password:
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql-bin.000001 | 333802 | | | |
+------------------+----------+--------------+------------------+-------------------+
|
5.主库备份,为从库的第一次数据同步准备数据
使用如下脚本产生数据库备份文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
#此处以备份wordpress数据库为例子
datadump=`which mysqldump`
mysqluser="root"
userpass="password"
wordpressdb="wordpress"
backupwordpress_sql=$wordpressdb.`date +%Y%m%d`.sql
if $datadump --master-data --single-transaction -u $mysqluser --password=$userpass -h localhost --opt $wordpressdb > $backupwordpress_sql 2>&1
then
echo " backup $wordpressdb success"
else
echo " backup $wordpressdb error"
exit 1
fi
#检验文件尾部是否存在 “-- Dump completed on”,如果存在不存在,则说明备份出错了。
if [ 0 -eq "$(sed '/^$/!h;$!d;g' $backupwordpress_sql | grep -c "Dump completed on")" ];
then
echo " backup $wordpressdb error"
exit 1
else
echo " backup $wordpressdb success"
fi
|
执行脚本,确保最后输出备份成功
1
2
3
|
$ cd ~
$ sudo bash backup_wordpress.sh
|
从服务器slave配置
1.修改mysql配置
1
|
$ sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
|
修改server-id,每个数据库的server-id要求是唯一的,不能相互冲突
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
[mysqld]
#设置server-id,必须唯一
server-id = 2
#日志也最好打开
log_bin = /var/log/mysql/mysql-bin.log
#如果日志开启了,最好把日志格式设置为row格式,这样如果主从数据不一致,可以尝试mysql flashback功能
binlog-format = row
#如果数据库是从5.7版本之前升级的,并且是wordpress那么会遇到无法更改数据库的情况,
#NO_ZERO_IN_DATE,NO_ZERO_DATE这两个参数限制的,我们需要去掉这个限制,原因在于
#wordpress创建的表中存在
#`comment_date` datetime(0) NOT NULL DEFAULT '0000-00-00 00:00:00',
#这样的定义是没办法进行后续的操作的,因此我们需要重新定义sql_mode来解除这个限制
sql_mode=ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
|
2.首次还原数据库:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
#停止可能访问数据库的应用
$ sudo service apache2 stop
$ sudo service php7.0-fpm stop
$ sudo service mysql restart
$ scp -P 22 -r root@121.199.27.227:~/wordpress.*.sql ./
#删除可能存在的一行警告信息,这行警告信息可能导致我们无法恢复数据
$ sed -i "/^mysqldump: \[Warning\] Using a password on the command line interface can be insecure\./d" wordpress.*.sql
$ mysql -u root -p -e "stop slave;"
$ mysql -u root -p -e "drop database wordpress;"
$ mysql -u root -p -e "create database wordpress;"
$ mysql -u root -p wordpress < wordpress.*.sql
|
还原完成后,把数据库设置成只读模式,如果从库可写会出现冲突导致同步失败
1
|
$ sudo vim /etc/mysql/mysql.conf.d/mysqld.cnf
|
添加如下语句:
1
2
3
4
5
6
7
|
[mysqld]
#数据库只读
#read_only = on
super_read_only = on
#tx_read_only = on
|
3.重启mysql,执行同步SQL语句(需要主服务器主机名,登陆凭据,二进制文件的名称和位置):
1
2
3
4
5
6
7
8
9
10
|
$ sudo service mysql restart
#只读模式
$ mysql -u root -p -e "set global read_only=1;"
#最好使用如下命令获得主库的起始位置
$ grep 'CHANGE MASTER TO MASTER_LOG_FILE' wordpress.*.sql | more
$ mysql -u root -p -e "CHANGE MASTER TO MASTER_HOST='121.199.27.227', MASTER_USER='repl', MASTER_PASSWORD='slavepass', MASTER_LOG_FILE='mysql-bin.000001',MASTER_LOG_POS=333802;"
|
4.启动slave同步进程:
1
|
$ mysql -u root -p -e "start slave;"
|
5.查看slave状态:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
$ mysql -u root -p -e "show slave status\G;"
Enter password:
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 121.199.27.227
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000001
Read_Master_Log_Pos: 9448236
Relay_Log_File: VM-114-251-ubuntu-relay-bin.000002
Relay_Log_Pos: 17780
Relay_Master_Log_File: mysql-bin.000001
Slave_IO_Running: Yes
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
...
|
当Slave_IO_Running和Slave_SQL_Running都为YES的时候就表示主从同步设置成功了。接下来就可以进行一些验证了,比如在主master数据库的test数据库的一张表中插入一条数据,在slave的test库的相同数据表中查看是否有新增的数据即可验证主从复制功能是否有效,还可以关闭slave(mysql>stop slave;),然后再修改master,看slave是否也相应修改(停止slave后,master的修改不会同步到slave),就可以完成主从复制功能的验证了。
还可以用到的其他相关参数:
master开启二进制日志后默认记录所有库所有表的操作,可以通过配置来指定只记录指定的数据库甚至指定的表的操作,具体在mysql配置文件的[mysqld]可添加修改如下选项:
1
2
3
4
5
6
7
|
# 不同步哪些数据库
binlog-ignore-db = mysql
binlog-ignore-db = test
binlog-ignore-db = information_schema
# 只同步哪些数据库,除此之外,其他不同步
binlog-do-db = game
|
如之前查看master状态时就可以看到只记录了test库,忽略了manual和mysql库。
参考链接
-
- 基于ssl的mysql的主从复制及排错
- MySQL主从复制(Master-Slave)实践
- Linux 配置mysql主从同步的iptables设置
- iptables 只拒绝某个ip的所有连接
- How to save rules of the iptables? [duplicate]
- MySQL主从复制与主主复制
- mysql-proxy 实现读写分离
- mysql主从同步不一致后的解决方法
- Replication breaks on delete from wordpress
- MySQL将某个数据库下的所有表的存储引擎修改为InnoDB类型语句
- mysql批量更新所有表的引擎为innodb
- mysql主从复制跳过错误
- FLUSH TABLES WITH READ LOCK 和 LOCK TABLES
- MySQL 5.7 下的对super用户只读