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之外,账号、密码、端口等看起来都是一样的。尽量为开发人员提供方便。