欢迎各位兄弟 发布技术文章
这里的技术是共享的
MySQL支持单向、异步复制,复制过程中一个服务器充当主服务器,而一个或多个其它服务器充当从服务器。主服务器将更新写入二进制日志文件,并维护日志文件的一个索引以跟踪日志循环。当一个从服务器连接到主服务器时,它通知主服务器从服务器在日志中读取的最后一次成功更新的位置。从服务器接收从那时起发生的任何更新,然后封锁并等待主服务器通知下一次更新。
为什么使用主从复制?
1、主服务器/从服务器设置增加了健壮性。主服务器出现问题时,你可以切换到从服务器作为备份。
2、通过在主服务器和从服务器之间切分处理客户查询的负荷,可以得到更好的客户响应时间。但是不要同时在主从服务器上进行更新,这样可能引起冲突。
3、使用复制的另一个好处是可以使用一个从服务器执行备份,而不会干扰主服务器。在备份过程中主服务器可以继续处理更新。
MySQL使用3个线程来执行复制功能(其中1个在主服务器上,另两个在从服务器上。当发出START SLAVE时,从服务器创建一个I/O线程,以连接主服务器并让主服务器发送二进制日志。主服务器创建一个线程将二进制日志中的内容发送到从服务器。从服务器I/O线程读取主服务器Binlog Dump线程发送的内容并将该数据拷贝到从服务器数据目录中的本地文件中,即中继日志。第3个线程是SQL线程,从服务器使用此线程读取中继日志并执行日志中包含的更新。SHOW PROCESSLIST语句可以查询在主服务器上和从服务器上发生的关于复制的信息。
默认中继日志使用host_name-relay-bin.nnnnnn形式的文件名,其中host_name是从服务器主机名,nnnnnn是序列号。用连续序列号来创建连续中继日志文件,从000001开始。从服务器跟踪中继日志索引文件来识别目前正使用的中继日志。默认中继日志索引文件名为host_name-relay-bin.index。在默认情况,这些文件在从服务器的数据目录中被创建。中继日志与二进制日志的格式相同,并且可以用mysqlbinlog读取。当SQL线程执行完中继日志中的所有事件后,中继日志将会被自动删除。
从服务器在数据目录中另外创建两个状态文件--master.info和relay-log.info。状态文件保存在硬盘上,从服务器关闭时不会丢失。下次从服务器启动时,读取这些文件以确定它已经从主服务器读取了多少二进制日志,以及处理自己的中继日志的程度。
下面来看一下具体的配置:
一、准备服务器
由于MySQL不同版本之间的(二进制日志)binlog格式可能会不一样,因此最好的搭配组合是Master的MySQL版本和Slave的版本相同或者更低,Master的版本肯定不能高于Slave版本。
本文中,我们假设主服务器(以下简称Master)和从服务器(以下简称Slave)的版本都是5.1.35,操作系统是Centos 4.7。
假设同步Master的主机名为:master(IP:192.168.1.123),Slave主机名为:slave(IP:192.168.1.124),2个MySQL的basedir目录都是/usr/local/mysql,datadir都是:/var/lib/mysql。
二、设置同步服务器
1、设置同步Master
修改 my.cnf 文件,在
# Replication Master Server (default)
# binary logging is required for replication
添加如下内容:
#log-bin=/var/log/mysql/updatelog server-id = 1 auto_increment_increment = 2 auto_increment_offset = 1 binlog-do-db=discuz binlog-ignore-db=mysql #log-slave-updates slave-skip-errors=all
注:
slave-skip-errors 是跳过错误,继续执行复制操作
多主互备和主从复制有一些区别,因为多主中 都可以对服务器有写权限,所以设计到自增长重复问题
出现的问题(多主自增长ID重复)
1:首先我们通过A,B的test表结构
2:掉A,在B上对数据表test(存在自增长ID)执行插入操作,返回插入ID为1
3:后停掉B,在A上对数据表test(存在自增长ID)执行插入操作,返回的插入ID也是1
4:然后 我们同时启动A,B,就会出现主键ID重复
解决方法:
我们只要保证两台服务器上插入的自增长数据不同就可以了
如:A查奇数ID,B插偶数ID,当然如果服务器多的话,你可以定义算法,只要不同就可以了
在这里我们在A,B上加入参数,以实现奇偶插入
A:my.cnf上加入参数
auto_increment_offset = 1 auto_increment_increment = 2
这样A的auto_increment字段产生的数值是:1, 3, 5, 7, …等奇数ID了
B:my.cnf上加入参数
auto_increment_offset = 2 auto_increment_increment = 2
这样B的auto_increment字段产生的数值是:2, 4, 6, 8, …等偶数ID了
可以看出,你的auto_increment字段在不同的服务器之间绝对不会重复,所以Master-Master结构就没有任何问题了。当然,你还可以使用3台,4台,或者N台服务器,只要保证auto_increment_increment = N 再设置一下auto_increment_offset为适当的初始值就可以了,那样,我们的MySQL可以同时有几十台主服务器,而不会出现自增长ID重复。
重启MySQL,创建一个MySQL帐号为同步专用
# /usr/local/mysql/bin/mysql -u root -p mysql> GRANT REPLICATION SLAVE ON *.* TO 'back'@'%' IDENTIFIED BY 'back'; mysql>GRANT FILE,SELECT,REPLICATION SLAVE ON *.* TO 'back'@'%' IDENTIFIED BY 'back'; mysql> FLUSH PRIVILEGES ;
如果想要在Slave上有权限执行 "LOAD TABLE FROM MASTER" 或 "LOAD DATA FROM MASTER" 语句的话,必须授予全局的 FILE 和 SELECT 权限:
2、设置同步Slave
修改my.cnf文件,添加
server-id = 2 auto_increment_increment = 2 auto_increment_offset = 2 master-host = 192.168.1.123 master-user = back master-password = back master-port = 3306 replicate-ignore-db=mysql replicate-do-db=discuz #log-slave-updates slave-skip-errors=all
重启MySQL
3、启动同步
在主服务器master MySQL命令符下:
# /usr/local/mysql/bin/mysql -u root -p mysql> show master status; 显示(当然这个是我机器的情况,你的不可能跟我一样哈,只是个例子): +------------------+----------+-------------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+-------------------+------------------+ | mysql-bin.000009 | 98 | discuz | mysql | +------------------+----------+-------------------+------------------+
在从服务器master MySQL命令符下:
# /usr/local/mysql/bin/mysql -u root -p mysql> slave stop; mysql> change master to master_host='192.168.1.123', master_user='back', master_password='back', master_log_file='mysql-bin.000009', master_log_pos=98; mysql> slave start; mysql> show slave status\G; Slave_IO_Running: Yes Slave_SQL_Running: Yes
如果都是yes,那代表已经在同步
往表里面写点数据测试一下看是否同步成功,如果不成功,绝对不是你的RP问题,再检查一下操作步骤!
4、设置双向同步
修改slave服务器的my.cnf,添加
log-bin=/var/log/mysql/updatelog binlog-do-db=discuz binlog-ignore-db=mysql
重启MySQL,创建一个MySQL帐号为同步专用
mysql> GRANT REPLICATION SLAVE ON *.* TO 'back'@'%' IDENTIFIED BY 'back'; mysql> GRANT FILE,SELECT,REPLICATION SLAVE ON *.* TO 'back'@'%' IDENTIFIED BY 'back'; mysql> FLUSH PRIVILEGES ;
修改master服务器的my.cnf,添加
master-host = 192.168.1.124 master-user = back master-password = back master-port = 3306 replicate-ignore-db=mysql replicate-do-db=discuz
重启MySQL
在主服务器slave MySQL命令符下:
show master status; +------------------+----------+-------------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+-------------------+------------------+ | mysql-bin.000013 | 98 | discuz | mysql | +------------------+----------+-------------------+------------------+
在服务器A MySQL命令符下:
mysql> slave stop; mysql> change master to master_host='192.168.1.124', master_user='back', master_password='back', master_log_file='mysql-bin.000013', master_log_pos=98; mysql> slave start;
其实也就是A->B单向同步的反向操作!双向同步,就这么简单啦!
提示:如果修改了主服务器的配置,记得删除从服务器上的master.info文件。否则从服务器使用的还是老配置,可能会导致错误。
-----------------------------------------------------------------------------------
注意:关于要复制多个数据库时,binlog-do-db和replicate-do-db选项的设置,网上很多人说是用半角逗号分隔,经过测试,这样的说法是错误的,MySQL官方文档也明确指出,如果要备份多个数据库,只要重复设置相应选项就可以了。
比如:
binlog-do-db=a binlog-do-db=b replicate-do-db=a replicate-do-db=b
补:
#启用从库日志,这样可以进行链式复制 log-slave-updates #从库是否只读,0表示可读写,1表示只读 <br />read-only=1 #只复制某个表 replicate-do-table=tablename <br /> #只复制某些表(可用匹配符) replicate-wild-do-table=tablename% #只复制某个库 replicate-do-db=dbname #只复制某些库 replicte-wild-do-db=dbname% #不复制某个表 replicate-ignore-table=tablename #不复制某些表 replicate-wild-ignore-table=tablename% #不复制某个库 replicate-ignore-db=dbname #复制完的sql语句是否立即从中继日志中清除,1表示立即清除 relay-log-purge=1
第一部分:主从关系的同步
做开发的时候要做MySQL的数据库同步,两台安装一样的系统,都是FreeBSD5.4,安装了Apache 2.0.55和PHP 4.4.0,MySQL的版本是4.1.15,都是目前最新的版本。
1. 安装配置
两台服务器,分别安装好MySQL,都安装在 /usr/local/MySQL 目录下(安装步骤省略,请参考相关文档),两台服务器的IP分别是192.168.0.1和192.168.0.2,我们把192.168.0.1作为 master数据库,把192.168.0.2作为slave服务器,我们采用单向同步的方式,就是master的数据是主的数据,然后slave主动去 master哪儿同步数据回来。
两台服务器的配置一样,我们把关键的配置文件拷贝一下,默认的配置文件是在 /usr/local/MySQL/share/MySQL目录下,分别有 my-large.cnf, my-medium.cnf, my-small.cnf等几个文家,我们只是测试,使用my-medium.cnf就行了。MySQL安装完后,默认的配置文件是指定在数据库存放目录下的,我们用的是4.1.X的,所以配置文件就应该在 /usr/local/MySQL/var 目录下,于是把配置文件拷贝过去:
cp /usr/local/MySQL/share/MySQL/my-medium.cnf /usr/local/MySQL/var/my.cnf |
两台服务器做相同的拷贝配置文件操作。
2. 配置Master服务器
我们要把192.168.0.1配置为主MySQL服务器(master),那么我们就要考虑我们需要同步那个数据库,使用那个用户同步,我们这里为了简单起见,就使用root用户进行同步,并且只需要同步数据库abc。
打开配置文件:
vi /usr/local/MySQL/var/my.cnf |
找到一下信息:
# required unique id between 1 and 2^32 - 1 # defaults to 1 if master-host is not set # but will not function as a master if omitted server-id = 1 //1为master,2为salve |
添加两行:
sql-bin-update-same //同步形式 binlog-do-db = abc //要同步的数据库 |
重启192.168.0.1的MySQL服务器:
/usr/local/MySQL/bin/MySQLadmin shutdown /usr/local/MySQL/bin/MySQLd_safe --user=MySQL & |
3. 配置Slave服务器
我们的slave服务器主要是主动去master服务器同步数据回来,我们编辑配置文件:
vi /usr/local/MySQL/var/my.cnf |
找到下面类似的信息:
# required unique id between 1 and 2^32 - 1 # defaults to 1 if master-host is not set # but will not function as a master if omitted server-id = 1 |
把上面的server-id修改为2,同时添加一些信息:
server-id = 2 //本MySQL是slave服务器 master-host = 192.168.0.1 //master服务器的IP master-user = root //连接master服务器的用户 master-password = '' //连接master服务器的密码 master-port = 3306 //连接端口 master-connect-retry = 10 //重试次数 replicate-do-db = abc //要同步的数据库 log-slave-updates //同步的形式 |
重启192.168.0.2的MySQL服务器:
/usr/local/MySQL/bin/MySQLadmin shutdown /usr/local/MySQL/bin/MySQLd_safe --user=MySQL & |
4. 测试安装
首先查看一下slave的主机日志:
cat /usr/local/MySQL/var/xxxxx_err (xxx是主机名) |
检查是否连接正常, 看到类似这样的信息就成功了
051031 11:42:40 MySQLd started 051031 11:42:41 InnoDB: Started; log sequence number 0 43634 /usr/local/MySQL/libexec/MySQLd: ready for connections. Version: '4.1.15-log' socket: '/tmp/MySQL.sock' port: 3306 Source distribution 051031 11:42:41 [Note] Slave SQL thread initialized, starting replication in log 'FIRST' at position 0, relay log './new4-relay-bin.000001' position: 4 051031 11:43:21 [Note] Slave I/O thread: connected to master 'root@192.168.0.1:3306', replication started in log 'FIRST' at position 4 |
在Master查看信息
/usr/local/MySQL/bin/MySQL -u root |
查看master状态:
MySQL> show master status; |
查看Master下MySQL进程信息:
MySQL> show processlist; |
在slave上查看信息:
/usr/local/MySQL/bin/MySQL -u root |
查看slave状态:
MySQL> show slave status; |
查看slave下MySQL进程信息:
MySQL> show processlist; |
你再在master的abc库里建立表结构并且插入数据,然后检查slave有没有同步这些数据,就能够检查出是否设置成功。
第二部分:双向关系的同步
master端 192.168.0.1slave端 192.168.0.2
1。MASTER端a.进入mysql,创建一个数据库abc:
create database abc;
b.创建一个用来同步的用户,指定只能在192.168.0.2登录:
grant replication slave on *.* to 'ha'@'192.168.0.2' identified by 'hapwd';c.修改master端的/etc/my.cnf文件 log-bin
server-id = 1
sql-bin-update-same 同步模式 ,在mysql5以上的版本都不需要这句,否则会启动不了mysql服务
binlog-do-db= abc 设置同步数据库,如果有多个数据库,每个数据库一行
binlog-ignore-db = mysql 设置不要同步的数据库,如有多个数据库,每个数据库一行
master-host=192.168.0.2
master-user=ha
master-password='hapwd'
master-port=3306
master-connect-retry=10
replicate-do-db=abc 设置要接收的数据库,如有多个数据库,每个数据库一行
replicate-ignore-db= mysql 设置不要接收的数据库,每个数据库一行 (一般这条可以不写)
log-slave-updates 在mysql5以上的版本都不需要这句
d.重启mysqle.进入mysql,执行: slave start;
2。SLAVE端a.进入mysql,创建一个数据库abc:
create database abc;
b.创建一个用来同步的用户,指定只能在192.168.0.1登录:
grant replication slave on *.* to 'ha'@'192.168.0.1' identified by 'hapwd';c.修改slave端的/etc/my.cnf文件 log-bin server-id = 2
binlog-do-db= abc 设置同步数据库,如果有多个数据库,每个数据库一行
binlog-ignore-db = mysql 设置不要同步的数据库,如有多个数据库,每个数据库一行
sql-bin-update-same 同步模式 ,在mysql5以上的版本都不需要这句,否则会启动不了mysql服务
master-host=192.168.0.1
master-user= ha
master-password='hapwd'
master-port=3306
master-connect-retry=10
replicate-do-db=abc 设置要接收的数据库,如有多个数据库,每个数据库一行
replicate-ignore-db= mysql 设置不要接收的数据库,每个数据库一行 (一般这条可以不写)
log-slave-updates 在mysql5以上的版本都不需要这句
d.重启mysqle.进入mysql,执行: slave start;
注意:1。如果有一端修改了数据后,另一端接收不到,手工执行命令load data from master;就可以了2。如果因为误删了二进制日志文件导致无法同步,则执行:(这几步很有用,你甚至可以把slave上的abc库删除掉,他会自动从master上复制过来重建)
slave stop;
reset slave;slave start;3。我们经常会在reset slave后发现重新同步时会出现重复数据无法倒入的情况,
比如:
Last_Errno: 1062
Last_Error: Error 'Duplicate entry '1472533' for key 1' on query. Default database: 'epg'. Query: 'insert into boot_info(stbid,begintime) values('zk124070c819','1259128442')'
这时可以这样,跳过冲突的这一行:
slave stop;
set GLOBAL SQL_SLAVE_SKIP_COUNTER=1;
slave start;