一、要求
1、配置现有的一台MySQL服务器为主服务器,另一台作为其从服务器
2、其中Master服务器允许查询和写入,Slave只允许查询
二、方案
使用3台RHEL6.5虚拟机,如下图所示。其中192.168.4.10是MySQL主服务器,负责提供同步源;192.168.4.20是MySQL从服务器,通过调取主服务器上的binlog日志,在本地重做对应的库、表,实现与主服务器的同步;192.168.4.100是客户机,用来登录数据库验证试验结果,也可以不用客户机而直接在从和主上操作数据库。
三、实现
1、准备工作
为两台MySQL服务器安装MySQL-server、MySQL-client软件包并为数据库修改root密码,客户机上只需安装MySQL-client。这里用的软件包包含在了 MySQL-5.6.15-1.el6.x86_64.rpm-bundle.tar里(点击可下载)
如果是两台服务器是克隆的,那么它们mysql服务器的server-uuid值可能相同,可以用下面两条命令查看一下
[root@slave ~]# cat /var/lib/mysql/auto.cnf
[auto]
server-uuid=c8b982e3-336b-11e4-9780-525400f9a647
要是相同,可以用uuidgen重新生成一个uuid,以替换auto.cnf里的server-uuid
[root@client ~]# uuidgen
bad15423-2ec5-4204-85b5-8d4027e05408
[root@master ~]# cat /var/lib/mysql/auto.cnf
[auto]
server-uuid=bad15423-2ec5-4204-85b5-8d4027e05408
修改配置文件并重启mysql服务以启用binlog日志
[root@master ~]# vim /etc/my.cnf
... ...
log-bin=mysql-bin
[root@master ~]# service mysql restart
Shutting down MySQL.... [确定]
Starting MySQL.... [确定]
在master上创建grade库来假设主服务器上已有的数据
[root@master ~]# mysql -uroot -p
Enter password: //以root身份登录,输入root用户密码
... ...
mysql> create database grade;
Query OK, 1 row affected (0.00 sec)
mysql> create table grade.math ( name varchar(20),score float(4,1));
Query OK, 0 rows affected (1.36 sec)
mysql> insert into grade.math values ( "jim" ,80.5);
Query OK, 1 row affected (0.17 sec)
mysql> select * from grade.math;
+------+-------+
| name | score |
+------+-------+
| jim | 80.5 |
+------+-------+
1 row in set (0.00 sec)
2、配置主服务器master (192.168.4.10)
1)修改配置文件/etc/my.cnf,指定服务器ID号、允许日志同步,重启mysql服务
[root@master mysql]# cat /etc/my.cnf
[mysqld]
datadir=/var/lib/mysql //数据库主目录
socket=/var/lib/mysql/mysql.sock
user=mysql
server_id=10 //指定服务器ID号
log-bin=mysql-bin //启用binlog日志并指定前缀
sync-binlog=1 //允许日志同步
... ...
[root@master mysql]# service mysql restart
Shutting down MySQL.. [确定]
Starting MySQL. [确定]
2)新建一个备份用户,授予复制权限,允许其从slave服务器访问
mysql> GRANT REPLICATION SLAVE ON *.* TO 'replicater'@'192.168.4.%' IDENTIFIED BY 'pwd123';
Query OK, 0 rows affected (0.07 sec)
3)检查master服务器的同步状态
mysql> SHOW MASTER STATUS\G;
*************************** 1. row ***************************
File: mysql-bin.000002 //当前的日志文件名
Position: 334 //当前记录的位置
Binlog_Do_DB:
Binlog_Ignore_DB:
Executed_Gtid_Set:
1 row in set (0.00 sec)
3、配置从服务器slave(192.168.4.20)
1)修改配置文件/etc/my.cnf,指定服务器ID号,重启mysql服务。在生产环境中还可以根据更MySQL手册设置更详细的选项。
[root@slave mysql]# vim /etc/my.cnf
[mysqld]
datadir=/var/lib/mysql
socket=/var/lib/mysql/mysql.sock
user=mysql
server_id=20
log-bin=slave //启用binlog日志并指定前缀
... ...
[root@slave mysql]# service mysql restart
Shutting down MySQL.. [确定]
Starting MySQL. [确定]
2)登录mysql,发起同步
通过CHANGE MASTER语句指定MASTER服务器的IP地址、同步用户名/密码、起始日志文件、偏移位置
mysql> CHANGE MASTER TO MASTER_HOST='192.168.4.10',
-> MASTER_USER='replicater',
-> MASTER_PASSWORD='pwd123',
-> MASTER_LOG_FILE='mysql-bin.000001', //对应Master的日志文件
-> MASTER_LOG_POS=1; ////对应Master的日志偏移位置
如果起始日志文件、偏移位置按照上面的写法,那么可以同步主服务器上自启用binlog日志以来对数据库所做的操作。
也可以在主服务器上SHOW MASTER STATUS\G; 按照当前状态填写,这时只同步master从这一刻以后对数据库的操作,以前的库和表不会同步,可以选择手动同步。当现有库、表都采用MyISAM引擎时,可执行离线导入导出,这样更有效率。否则可使用mysqldump等工具来实现 库的导入导出。
然后启动slave
mysql> START SLAVE;
Query OK, 0 rows affected (0.05 sec)
一旦启用SLAVE复制,当需要修改MASTER信息时,应先执行STOP SLAVE停止复制,然后重新修改、启动复制。
通过上述连接操作,MASTER服务器的设置信息自动存为master.info文件,以后每次MySQL服务程序时会自动调用并更新,无需重复设置。查看master.info文件的开头部分内容,可验证相关设置:
[root@slave mysql]# head /var/lib/mysql/master.info
23
mysql-bin.000001 //对应Master的日志文件
120
192.168.4.10 //master的ip地址
replicater //与master同步的用户名
pwd123 //密码
3306 //端口
60
0
mysql> SHOW SLAVE STATUS\G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.4.10
Master_User: replicater
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000002
Read_Master_Log_Pos: 334
Relay_Log_File: slave-relay-bin.000003
Relay_Log_Pos: 497
Relay_Master_Log_File: mysql-bin.000002
Slave_IO_Running: Yes //IO线程应该已运行
Slave_SQL_Running: Yes //SQL线程应该已运行
Replicate_Do_DB:
Replicate_Ignore_DB:
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: 334
Relay_Log_Space: 1283
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:
Replicate_Ignore_Server_Ids:
Master_Server_Id: 10
Master_UUID: bad15423-2ec5-4204-85b5-8d4027e05408
Master_Info_File: /var/lib/mysql/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set:
Executed_Gtid_Set:
Auto_Position: 0
1 row in set (0.00 sec)
若START SLAVE直接报错失败,请检查CHANGE MASTER相关设置是否有误,纠正后再重试;若IO线程或SQL线程有一个为“No”,则应检查服务器的错误日志,分析并排除故障后重启主从复制。
4、测试主从同步效果
在主服务器上授权一个用户,会自动同步到slave上,用户用于在客户端(192.168.4.100)登录:
mysql> GRANT ALL ON grade.* TO "user01"@"192.168.4.%" IDENTIFIED BY "pwd123" ;
Query OK, 0 rows affected (0.12 sec)
在客户端上登录从库
在从上发现grade库已经同步过来
[root@client ~]# mysql -h192.168.4.20 -uuser01 -ppwd123
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| grade |
| test |
+--------------------+
3 rows in set (0.00 sec)
mysql> SELECT * FROM grade.math;
+------+-------+
| name | score |
+------+-------+
| jim | 80.5 |
+------+-------+
1 row in set (0.00 sec)
1)在客户端用user01登录Master数据库
在grade库的math表里随意插入几条表记录:
[root@client 桌面]# mysql -h 192.168.4.10 -uuser01 -ppwd123
... ...
mysql> INSERT INTO grade.math VALUES ("tom",78),("lily",90);
Query OK, 2 rows affected (0.25 sec)
Records: 2 Duplicates: 0 Warnings: 0
mysql> SELECT * FROM grade.math;
+------+-------+
| name | score |
+------+-------+
| jim | 80.5 |
| tom | 78.0 |
| lily | 90.0 |
+------+-------+
3 rows in set (0.00 sec)
2)在Slave上确认自动同步的结果
直接查询math表的记录,应该与Master上的一样,这才说明主从同步已经成功生效:
[root@client 桌面]# mysql -h 192.168.4.20 -uuser01 -ppwd123
... ...
mysql> SELECT * FROM grade.math;
+------+-------+
| name | score |
+------+-------+
| jim | 80.5 |
| tom | 78.0 |
| lily | 90.0 |
+------+-------+
3 rows in set (0.00 sec)
3)在Master服务器上可查看Slave主机的信息
mysql> SHOW SLAVE HOSTS;
+-----------+------+------+-----------+--------------------------------------+
| Server_id | Host | Port | Master_id | Slave_UUID |
+-----------+------+------+-----------+--------------------------------------+
| 20 | | 3306 | 10 | c8b982e3-336b-11e4-9780-525400f9a647 |
+-----------+------+------+-----------+--------------------------------------+
1 row in set (0.00 sec)
5、将slave服务器设为只读
一般来说,为了避免写入冲突,采用主、从复制结构时,不应该允许用户从Slave执行数据库写入操作,这样会导致双方数据的不一致性。
正因为如此,我们可以把Slave数据库限制为只读模式,这种情况下有SUPER权限的用户和SLAVE同步线程才能写入。相关验证操作及效果可参考以下过程。
1)未启用只读前,验证从slave写入
在客户端上以user01身份登录slave,并写入数据:
[root@client 桌面]# mysql -h 192.168.4.20 -uuser01 -ppwd123
... ...
mysql> INSERT INTO grade.math VALUES ("bob",60);
Query OK, 1 row affected (0.09 sec)
在slave上可以看到新插入的数据:
mysql> SELECT * FROM grade.math;
+------+-------+
| name | score |
+------+-------+
| jim | 80.5 |
| tom | 78.0 |
| lily | 90.0 |
| bob | 60.0 |
+------+-------+
4 rows in set (0.00 sec)
mysql> QUIT
Bye
但是在master上却看不到,导致主、从上的math表数据不一致
[root@client 桌面]# mysql -h 192.168.4.10 -uuser01 -ppwd123
... ...
mysql> SELECT * FROM grade.math;
+------+-------+
| name | score |
+------+-------+
| jim | 80.5 |
| tom | 78.0 |
| lily | 90.0 |
+------+-------+
3 rows in set (0.00 sec)
mysql> quit
Bye
完成上述验证后,在slave上删除刚刚插入的记录,确保主从数据一致
[root@client 桌面]# mysql -h 192.168.4.20 -uuser01 -ppwd123
... ...
mysql> DELETE FROM grade.math WHERE name="bob";
Query OK, 1 row affected (0.11 sec)
mysql> SELECT * FROM grade.math;
+------+-------+
| name | score |
+------+-------+
| jim | 80.5 |
| tom | 78.0 |
| lily | 90.0 |
+------+-------+
3 rows in set (0.00 sec)
mysql> QUIT
Bye
2)修改slave的/etc/my.cnf文件,重启mysql服务
[root@slave mysql]# vim /etc/my.cnf
[mysqld]
... ...
read_only=1 //启动只读模式
[root@slave mysql]# service mysql restart
Shutting down MySQL.. [确定]
Starting MySQL.. [确定]
3)再次在slave上验证数据库写入操作
在client(192.168.4.100)上以user01用户登录从库
[root@client 桌面]# mysql -h 192.168.4.20 -uuser01 -ppwd123
... ...
mysql> USE grade; //切换到grade库,
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> INSERT INTO math VALUES ("lucy",99);
ERROR 1290 (HY000): The MySQL server is running with the --read-only option so it cannot execute this statement //表的写操作失败,提示数据库运行在只读模式下
四、扩展
指定哪些库参与主从复制
有两种途径:
u 在Master上限制,采用binlog-do-db、binlog-ignore-db选项,指定对哪些库记录或不记录二进制日志,不记录的自然就无法被Slave读取,从而也就相当于不参与同步。
u 在Slave上限制,采用replicate-do-db、replicate-ignore-db选项,指定对哪些库执行复制或排除复制。
上述设置参数中,记录与不记录属于互斥选项,不要同时设置;复制与不复制也是互斥选项,不要同时设置。
当设置多条replicate-do-db或replicate-ignore-db时,需要特别注意:这种情况下Master的跨库操作(比如UPDATE 库名.表名 .. ..)不会被同步,从而易导致后续同步报错中断。要解决这个问题,可改用(或合用)以下两个选项:
replicate-wild-do-table=库名.%
replicate-wild-ignore-table=库名.%
以只同步test库为例,相关操作及效果参考如下:
1)在slave上修改/etc/my.cnf文件,只同步test库
[root@slave mysql]# vim /etc/my.cnf
[mysqld]
... ...
replicate-do-db=mysql //同步mysql库
replicate-wild-do-table=mysql.% //含跨库更新,但是跨的库只允许在可以同步的库列表里,比如在这里只允许在test库或mysql库里更新mysql库
replicate_do_db=test
replicate_wild_do_table=test.%
//其他未指定的库将被忽略
... ...
[root@slave mysql]# service mysql restart
Shutting down MySQL.... [确定]
Starting MySQL.. [确定]
2)在master上分别操作test库、grade库
在test库中新建t1表
mysql> USE mysql;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> CREATE TABLE test.t1(id int(4),name varchar(20));
Query OK, 0 rows affected (0.93 sec)
mysql> INSERT INTO test.t1 VALUES(1,"mike");
Query OK, 1 row affected (0.17 sec)
mysql> SELECT * FROM test.t1;
+------+------+
| id | name |
+------+------+
| 1 | mike |
+------+------+
1 row in set (0.00 sec)
在grade中新建English表:
mysql> CREATE TABLE grade.English (name varchar(20),score float(3,1));
Query OK, 0 rows affected (1.15 sec)
mysql> INSERT INTO grade.English VALUES ("harry",60);
Query OK, 1 row affected (0.17 sec)
mysql> SELECT * FROM grade.English;
+-------+-------+
| name | score |
+-------+-------+
| harry | 60.0 |
+-------+-------+
1 row in set (0.00 sec)
3)在slave上观察同步结果
Master上对test库的操作已经同步到slave:
mysql> SELECT * FROM test.t1;
+------+------+
| id | name |
+------+------+
| 1 | mike |
+------+------+
1 row in set (0.00 sec)
Master上对grade库的操作被slave忽略
mysql> SELECT * FROM grade.English;
ERROR 1146 (42S02): Table 'grade.English' doesn't exist
五、/etc/my.cnf常用的配置选项
适用于Master服务器:
binlog-do-db=name 设置Master对那些库记日志
binlog-ignore-db=name 设置Master对那些库不记日志
适用于Slave服务器:
log-slave-updates 记录从库更新,允许链式复制(A-B-C)
relay-log=slave1-relay-bin 指定中继日志文件名
replicate-do-db=mysql 仅复制指定库,其他库将被忽略,此选项可设置多条(省略时复制所有库)
replicate-ignore-db=test 不复制哪些库,其他库将被忽略(与上一条do-db冲突,只需选用其中一条)
report-host=slave1 报告给Master的主机名或IP地址
slave-net-timeout=60 出现网络中断时,重试超时(默认为60秒)