MySQL主从复制实践讲解

2012年写的笔记,部分内容可能已经过时,仅供参考。

1 复制准备

1.1. 定义服务器角色

主库(mysql master): 192.168.65.133 port:3306

从库(mysql slave): 192.168.65.133 port:3307

1.2. 数据库环境准备

具备单机单数据库多实例的环境

或两台服务器,每个机器一个数据库的环境。

1.3. 数据库讲法的约定

主库(master),从库(slave)

2. 主库上执行操作

2.1. 设置server-id值并开启binlog参数

复制的关键就是binlog日志。

执行vi /data/3306/my.cnf,修改下面两个参数:

[mysqld]
log-bin = /data/3306/mysql-bin
server-id = 1

提示:
(1)	上面参数要放在[mysqld]模块下,否则会出错。
(2)	server-id值取服务器ip地址的最后8位,目的是避免不同机器或实例ID重复(不适合多实例)。
(3)	先在文件中查找相关参数按要求修改,不存在时再修改参数,切记,参数不能重复。
(4)	修改my.cnf后需要重起数据库命令:/data/3306/mysql restart,注意要确认真正重起了。

检查配置后的结果:

[root@stu412 ~]# grep -E "server-id|log-bin" /data/3306/my.cnf
log-bin = /data/3306/mysql-bin #log-bin后面可以不带=号内容,mysql会使用默认日志
server-id = 1

2.2. 建立同步的账号rep

登录mysql 3306实例数据库

[root@stu412 ~]# mysql -uroot -p'123456' -S /data/3306/mysql.sock
mysql> grant replication slave on *.* to 'rep'@'192.168.65.%' identified by '123456';
Query OK, 0 rows affected (0.02 sec)

# replication slave为mysql同步的必须权限,此处不要授权all
# *.* 表示所有库所有表,你可以指定具体的库和表进行复制。
# 'rep'@'192.168.65.%' 中rep表同步账号,'192.168.65.%'为授权主机,
# %表示允许整个192.168.65.0网段以rep用户访问。
# '123456'表示密码,实际环境要设置复杂一点。

检查rep账号
mysql> select user,host from mysql.user;
+------+--------------+
| user | host         |
+------+--------------+
| root | 127.0.0.1    |
| rep  | 192.168.65.% |
| root | localhost    |
+------+--------------+
3 rows in set (0.00 sec)
#或使用 show grants for 'rep'@'192.168.65.%';

2.3. 对数据库锁表只读(当前窗口不要关闭)

生产环境时,操作主从复制,是需要锁表停机时间的。锁表会影响业务。

mysql> flush tables with read lock;
Query OK, 0 rows affected (0.00 sec)

# 这个锁表命令的时间,在不同的引擎的情况下,会受下面参数的控制,
# 锁表时,如果超过设置时间不操作会自动解锁。
interactive_timeout		28800
wait_timeout				28800

mysql> show variables like '%timeout%';
+----------------------------+-------+
| Variable_name              | Value |
+----------------------------+-------+
| connect_timeout            | 10    |
| delayed_insert_timeout     | 300   |
| innodb_lock_wait_timeout   | 120   |
| innodb_rollback_on_timeout | OFF   |
| interactive_timeout        | 28800 |
| net_read_timeout           | 30    |
| net_write_timeout          | 60    |
| slave_net_timeout          | 3600  |
| table_lock_wait_timeout    | 50    |
| wait_timeout               | 28800 |
+----------------------------+-------+
10 rows in set (0.00 sec)

2.4. 查看主库状态

mysql> show master status;
+------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000004 |      258 |              |                  |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)

2.5. 导出数据库数据

单开新窗口,导出数据库数据,如果数据量很大(100G+),并且允许停机,可以停库直接打包数据文件迁移。

[root@stu412 ~]# mkdir -p /server/backup
[root@stu412 ~]# mysqldump -uroot -p'123456' -S /data/3306/mysql.sock -A -B | gzip >/server/backup/mysql_bak.$(date +%F).sql.gz
[root@stu412 ~]# ls -lh /server/backup/
total 140K
-rw-r--r-- 1 root root 136K Sep  1 16:02 mysql_bak.2012-09-01.sql.gz

导库后,解锁主库,恢复可写
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)

2.6. 把主库备份的MYSQL数据迁移到从库

此时用到的命令可能有scp,rsync等。 本文讲解的是单数据库多实例的主从配置,因此,数据在一台机器上。

3. 从库上执行操作

3.1. 设置server-id值并关闭binlog参数

数据库的server-id一般在LAN内是唯一的,这里的server-id要和主库及其它从库不同,并注释掉从库的binlog参数配置。

vi /data/3307/my.cnf 编辑my.cnf配置文件:

[mysqld]
#log-bin = /data/3306/mysql-bin
server-id = 2

检查结果:

[root@stu412 ~]# grep -E "server-id|log-bin" /data/3307/my.cnf
#log-bin = /data/3307/mysql-bin
server-id = 3

重起从库:

/data/3307/mysql restart

3.2. 还原主库导出的数据到从库

[root@stu412 ~]# cd /server/backup/
[root@stu412 backup]# ll
total 140
-rw-r--r-- 1 root root 138278 Sep  1 16:02 mysql_bak.2012-09-01.sql.gz
[root@stu412 backup]# gzip -d mysql_bak.2012-09-01.sql.gz
[root@stu412 backup]# ls -l
total 500
-rw-r--r-- 1 root root 507318 Sep  1 16:02 mysql_bak.2012-09-01.sql
[root@stu412 backup]# mysql -uroot -p'123456' -S /data/3307/mysql.sock <mysql_bak.2012-09-01.sql

3.3. 登录从库配置同步参数

mysql -uroot -p'123456' -S /data/3307/mysql.sock
CHANGE MASTER TO
MASTER_HOST='192.168.65.133',
MASTER_PORT=3306,
MASTER_USER='rep',
MASTER_PASSWORD='123456',		
MASTER_LOG_FILE='mysql-bin.000004', 		#注意不能多空格
MASTER_LOG_POS=258;							#注意不能多空格


不登录数据库,在命令行快速执行CHANGE MASTER的语句(适合在脚本中批量建slave库)
mysql -uroot -p'123456' -S /data/3307/mysql.sock<< EOF
CHANGE MASTER TO
MASTER_HOST='192.168.65.133',
MASTER_PORT=3306,
MASTER_USER='rep',
MASTER_PASSWORD='123456',
MASTER_LOG_FILE='mysql-bin.000004',
MASTER_LOG_POS=258;
EOF

3.4. 启动从库同步开关

[root@stu412 backup]# mysql -uroot -p'123456' -S /data/3307/mysql.sock -e "start slave;"
[root@stu412 backup]# mysql -uroot -p'123456' -S /data/3307/mysql.sock -e "show  slave status\G;"

注:停止从库的命令: stop slave;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.65.133	#当前mysql master服务主机
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000004
Read_Master_Log_Pos: 258
Relay_Log_File: relay-bin.000002
Relay_Log_Pos: 251
Relay_Master_Log_File: mysql-bin.000004
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Replicate_Do_DB:
Replicate_Ignore_DB: mysql
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 0
Last_Error:
Skip_Counter: 0
Exec_Master_Log_Pos: 258
Relay_Log_Space: 400
Until_Condition: None
Until_Log_File:
Until_Log_Pos: 0
Master_SSL_Allowed: No
Master_SSL_CA_File:
Master_SSL_CA_Path:
Master_SSL_Cert:
Master_SSL_Cipher:
Master_SSL_Key:
Seconds_Behind_Master: 0	#和主库同步延迟的秒数,这个参数很重要
Master_SSL_Verify_Server_Cert: No
Last_IO_Errno: 0
Last_IO_Error:
Last_SQL_Errno: 0
Last_SQL_Error:

[root@stu412 backup]# mysql -uroot -p'123456' -S /data/3307/mysql.sock -e "show  slave status\G;" | egrep 'Slave_IO_Running|Slave_SQL_Running'
Slave_IO_Running: Yes
Slave_SQL_Running: Yes

判断复制是否成功,就要看IO和SQL两个线程是否显示为Yes状态

Slave_IO_Running: Yes  	#负责从库去主库读取BINLOG日志,并写入从库的中继日志中。
Slave_SQL_Running: Yes 	#负责读取并执行中继日志中的BINLOG,转换sql语句后应用到数据库汇总。

show slave status的参数说明请看mysql手册。

3.5. 测试复制结果

[root@stu412 backup]# mysql -uroot -p'123456' -S /data/3306/mysql.sock -e "create database ett;"  
[root@stu412 backup]# mysql -uroot -p'123456' -S /data/3306/mysql.sock -e "show databases like 'ett';"  
+----------------+
| Database (ett) |
+----------------+
| ett            |
+----------------+
[root@stu412 backup]# mysql -uroot -p'123456' -S /data/3306/mysql.sock -e "drop database ett;"  
[root@stu412 backup]# mysql -uroot -p'123456' -S /data/3306/mysql.sock -e "show databases like 'ett';"  
[root@stu412 backup]#

4. 生产环境主从库同步注意事项

4.1. 第一次做从库如何做?

服务器只有主库,且跑了线上应用了,此时可能需要申请停机维护时间。=>凌晨停机配置主从复制。

4.2. 无须熬夜在工作时间轻松配置从库

也可以不申请,在定时任务备份时,每天的夜里服务器压力小时候定时备份时做一些措施。如:

(1) 锁表备份全备一份。

(2) 锁表前后取得show master status值记录日志里。

这样就可以在白天从容的实现主从同步了。

实现脚本:


[root@stu412 server]# cat mysql_backup.sh
#!/bin/bash
MYUSER=root
MYPASS="123456"
MYSOCK=/data/3306/mysql.sock

MAIN_PATH=/server/backup
DATA_PATH=/server/backup
LOG_FILE=${DATA_PATH}/mysqllogs_`date +%F`.log
DATA_FILE=${DATA_PATH}/mysql_backup_`date +%F`.sql.gz

MYSQL_PATH=/usr/local/mysql/bin
MYSQL_CMD="$MYSQL_PATH/mysql -u$MYUSER -p$MYPASS -S $MYSOCK"
MYSQL_DUMP="$MYSQL_PATH/mysqldump -u$MYUSER -p$MYPASS -S $MYSOCK -A -B --flush-logs --single-transaction -e"

$MYSQL_CMD -e "flush tables with read lock;"
echo "---------show master status result---------" >> $LOG_FILE
$MYSQL_CMD -e "show master status;" >> $LOG_FILE
${MYSQL_DUMP} | gzip > $DATA_FILE
$MYSQL_CMD -e "unlock tables;"
mail -s "mysql slave log" qxl_work@163.com < $LOG_FILE

[root@stu412 server]# sh mysql_backup.sh
[root@stu412 backup]# cat mysqllogs_2012-09-01.log
---------show master status result---------
File    Position        Binlog_Do_DB    Binlog_Ignore_DB
mysql-bin.000004        418

4.3. 不停主库一键批量创建从库脚本

mysql_slave.sh


#!/bin/sh
################################################
#this scripts is created by oldboy
#oldboy QQ:31333741
#site:http://www.etiantian.org
#blog:http://oldboy.blog.51cto.com
#oldboy trainning QQ group: 208160987 226199307  44246017
################################################

MYUSER=root
MYPASS="oldboy"
MYSOCK=/data/3306/mysql.sock

MAIN_PATH=/server/backup
DATA_PATH=/server/backup
LOG_FILE=${DATA_PATH}/mysqllogs_`date +%F`.log
DATA_FILE=${DATA_PATH}/mysql_backup_`date +%F`.sql.gz

MYSQL_PATH=/usr/local/mysql/bin
MYSQL_CMD="$MYSQL_PATH/mysql -u$MYUSER -p$MYPASS -S $MYSOCK"

#recover
cd ${DATA_PATH}
gzip -d mysql_backup_`date +%F`.sql.gz
$MYSQL_CMD < mysql_backup_`date +%F`.sql

#config slave
cat |$MYSQL_CMD<< EOF
CHANGE MASTER TO  
MASTER_HOST='10.0.0.179',
MASTER_PORT=3306,
MASTER_USER='rep',
MASTER_PASSWORD='oldboy123',
MASTER_LOG_FILE='mysql-bin.000008',
MASTER_LOG_POS=342;
EOF

$MYSQL_CMD -e "show slave status\G"|egrep "IO_Running|SQL_Running" >$>LOG_FILE
mail -s "mysql slave result" 31333741@qq.com < $LOG_FILE

5. 相关mysql技术技巧概览

5.1. 配置忽略权限库同步参数

binlog-ignore-db=information_schema
binlog-ignore-db=mysql

5.2. 主从复制故障解决

show  slave  status报错:Error  xxx  don't  exist
且show  slave  status\G;
Slave_IO_Running: Yes
Slave_SQL_Running : No
Seconds_Behind_Master: NULL

解决方法:

stop  slave;
set  global  sql_slave_skip_counter=1; #忽略执行N个更新
start  slave;

5.3. 让MySQL slave记录binlog方法

在从库的my.cnf中加入如下参数

log-slave=updates
log-bin=mysql3307-bin
expize_logs_days = 7

应用场景:级联复制或从库做数据备份

5.4. 严格设置从库只读

read-only的妙用;

5.5. 生产环境如何确保从库只读

1)mysql从服务器中加入read-only参数或者在从服务器启动时加该参数;
2)忽略mysql库及information_schema库同步;
3)授权从库用户时仅授权select权限.

生产环境访问主库授权:

grant select,insert,update,delete on blog.* to 'blog'@'10.0.0.%' identified by '123456';

生产环境访问从库授权:

grant select on blog.* to 'blog'@'10.0.0.%' identified by '123456';

5.6. 生产环境读写分离的账户设置建议

主库(提供写):blog passwd ip:192.168.65.133 port:3306

从库(提供读):blog passwd ip:192.168.65.134 port:3306

除了IP之外,账号、密码、端口等看起来都是一样的。尽量为开发人员提供方便。