mysql m/s架构和mysqlm/m架构是mysql下的成熟解决方案了。网上相关文章也比较多,我在此趁着搬迁blog的机会,把相关知识总结分享一下。
相信能够看到这篇文章的朋友应该已经掌握了mysql的基本知识,我就不重复描述了,我就普及下和复制相关的理论知识。
数据分布(多个地区的数据分发)
负载均衡(读写分离)
备份
高可用和故障切换的核心功能
测试mysql升级
mysql复制的原理现阶段都是一样的,master将操作记录到bin-log中,slave的一个线程去master读取bin-log,并将他们保存到relay-log中,slave的另外一个线程去重放relay-log中的操作来实现和master数据同步。
mysql-3.2 开始支持基于命令的复制,也就是statement-based replication。
mysql-5.1 开始支持基于行的复制和混合复制,也就是row-based replication和mixed-based relication。
mysql-5.5 开始支持semi-synchronous的复制,也叫半同步复制,目的在于事务环境下保持主从一致。
mysql-5.6 开始支持延时复制。
这段内容来自homeserver.com.tw.
SBR:以sql语句的方式把master的操作记录到binlog中。
RBR:把修改的行记录到binlog中。
MBR:默认使用SBR方式记录日志,但是遇见一些特殊语句,如语句中包含uuid()、INSERT DELAYED、使用DML操作NDB表、大于等于两个AUTO_INCREMENT字段会被更新的时候都会使用RBR格式。具体参见:http://dev.mysql.com/doc/refman/5.1/en/binary-log-mixed.html
SBR优点:
1、稳定成熟
2、binlog文件较小
3、binlog中包含了所有资料库更改信息,方便数据库分析和恢复。
4、复制的master和slave,现在大部分版本都支持,不过建议slave的版本>=master的版本。
SBR缺点:
1、不是所有update语句都会被复制,尤其是包含不确定操作的时间。
2、调用具有不确定因数的UDF(user defaine function)时复制可能会出问题。
3、尽量避免使用以下函数:
LOAD_FILE();
UUID();
USER();
FUND_ROWS();
SYSDATE();
4、INSERT … SELECT 会产生比RBR更多的行级锁。
5、复制要进行全表扫描时(索引问题导致的),需要比RBR请求更多的行级锁。
6、对于有auto_increment字段的innodb表而言,insert语句会阻塞其他insert语句
7、对于一些复杂的语句,在从服务器上也会消耗很多资源。而RBR只会处理影响的行
8、确定的UDF也会在从服务器上执行。
9、slave数据必须和master一致,不然可能复制出错。
10、只支持REPEATABLE READ可重复读和以上的隔离级别
RBR的优点:
1、任何情况都可以复制,安全可靠。
2、和大多数数据库复制原理一样
3、多数情况下,从服务器上的表如果有主键的话,复制就会快很多。
4、复制以下几种语句行锁更少
INSERT … SELECT
包含AUTO_INCREMENT的INSERT
没有附带条件或者没有修改多条记录的UPDATE或DELETE语句
5、执行INSERT,UPDATE,DELETE语句时锁更少
6、从服务器上使用多线程复制成为可能
RBR的缺点:
1、binlog占用空间大。
2、复杂的回滚时binlog中会包含大量的数据。
3、服务器上执行update时所有发生改变的记录都会写到binlog中,而SBR只会写一条sql语句。
4、UDF产生的大BLOD值会导致复制变慢。
5、无法从binlog中看见复制写的语句。
6、当在非事务表上执行一段积累的SQL语句时,最好采用SBR模式,否则很容易导致主从数据不一致。
MIXED介绍
在使用MIXED格式下,mysql采用STATEMENT格式进行二进制日志文件的记录,但是有一些情况下会使用ROW格式,可能的情况如下:
1、表的存储引擎为NDB,这个时候DML操作都会以ROW格式记录
2、使用了uuid()、user(),current_user(),found_rows(),row_count(),等不确定函数。
3、使用了insert delay语句
4、使用了用户定于的函数(UDF)
5、使用了临时表(temporary table)
注意:针对系统库mysql里面的表发生变化的处理规则如下:
1、如果采用insert,update,delete直接操作表,则日志根据binlog_format设定的格式记录。
2、如果使用grant,revoke,set password等DCL语句,那么无论如何都会使用SBR模式记录。
3、blockhole引擎不支持row格式,ndb引擎不支持statement格式。
1、环境设定
role IP mysql-version binlog_format master 10.1.1.14 percona-mysql-5.5.20 statement slave 10.1.1.15 percona-mysql-5.5.20 statement
2、master和slave都安装mysql参见http://isadba.com/index.php/2011/12/percona-5-5%E5%AE%89%E8%A3%85%E7%AC%94%E8%AE%B0/
我这里使用的是mysql的分支版本percona数据库,其实和mysql官方的差不多。
好了,现在我们的mysql已经跑起来了。配置文件使用默认的my-innodb-heavy-4g.cnf,现在我们开始修改配置文件。
3、修改配置文件my.cnf
和复制相关的参数:
log-bin=mysql-bin //开启二进制日志,必须的。 binlog_format={stament|row|mixed} //二进制日志的格式,上面已经介绍了。 server-id= 1 //serverid,在一个复制架构中全局唯一,一般master是1开始 sync_binlog=1 //在master上开启,用来同步刷新数据到硬盘,如果开启数据安全性会提高很多,但是会牺牲性能,根据自己需求权衡吧 auto_increment_increment=2 //auto_increment每次增长的值,主主复制的时候用来,避免auto_increment发生冲突。 auto_increment_offset=1 //auto_increment增长的偏移量,这两个值一起的意思就是auto_increment从1开始,每次增长2个,也就是1,3,5,7.... skip_slave_start //slave数据库启动后,不自动开启slave线程。 read_only //禁止非super权限的用户修改永久表。在主主复制情况下,用来锁定一个master保证数据一致性。 log_slave_updates //slave上配置,将执行的sql语句记录到binlog,如果这个slave是其他db的master,需要开启这个选项。 slave_skip_errors=all //slave配置,自动跳过所有错误,不建议使用,这样不利于保证主从数据的一致性。 innodb_flush_logs_at_trx_commit=1 //innodb引擎时可用通常和sync_binlog=1一起使用 innodb_support_xa=1 //innodb引擎时使用,这两个参数的目的是保证主从数据的一致性。 以下这些参数比较少使用,功能大概就是复制指定的库或者过滤某个库不复制,要使用此功能的朋友需要自己再详细测试下。 我一般不使用这些参数,因为他们不太好控制,容易导致主从数据不一致。 binlog-do-db=test binlog-ignore-db=mysql replicate-do-db=test replicate-ignore-db=mysql replicate-do-table= replicate-ignore-table= replicate_wild_do_table= replicate_wild_ignore_table=mysql.%
更多参数参见http://dev.mysql.com/doc/refman/5.5/en/replication-options.html
修改配置文件
10.1.1.14:
主库使用默认的配置,主要检查是否有以下三条记录
log-bin=mysql-bin binlog_format=mixed server-id=1
10.1.1.15:
log-bin=mysql-bin binlog_format=mixed server-id=2 log_slave_updates skip_slave_start
配置完后需要检查这些选项是不是在配置文件中出现多次,因为mysql默认以最后一次出现的参数为准。
配置完成后从新启动mysql服务器。当然,现在不用重启10.1.1.14.
4、授权及配置复制
下面我们先以数据库没有数据为例演示
10.1.1.14: mysql> grant replication slave,file on *.* to 'slave'@'10.1.1.15' identified by 'slave'; Query OK, 0 rows affected (0.01 sec) mysql> flush privileges; Query OK, 0 rows affected (0.01 sec) mysql> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000003 | 337 | | | +------------------+----------+--------------+------------------+ 1 row in set (0.00 sec) 10.1.1.15: mysql> CHANGE MASTER TO -> MASTER_HOST='10.1.1.14', -> MASTER_USER='slave', -> MASTER_PASSWORD='slave', -> MASTER_PORT=9188, -> MASTER_LOG_FILE='mysql-bin.000003', -> MASTER_LOG_POS=337, -> MASTER_CONNECT_RETRY=10; Query OK, 0 rows affected (0.00 sec) mysql> start slave; Query OK, 0 rows affected (0.00 sec) mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.1.1.14 Master_User: slave Master_Port: 9188 Connect_Retry: 10 Master_Log_File: mysql-bin.000003 Read_Master_Log_Pos: 337 Relay_Log_File: zj14-relay-bin.000002 Relay_Log_Pos: 253 Relay_Master_Log_File: mysql-bin.000003 Slave_IO_Running: Yes Slave_SQL_Running: Yes 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: 337 Relay_Log_Space: 408 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: 1 1 row in set (0.00 sec) 当看见如下内容的时候证明我们复制搭建成功,复制开始运行了。 Slave_IO_Running: Yes Slave_SQL_Running: Yes
我们这里是两个新安装的数据库,如果是已经有数据的数据库你需要
a、将主库锁表后(flush tables with read lock\G),
b、备份主库数据
c、记录主库当前的show master status;
d、然后unlock tables;解除锁定。
e、slave端恢复master刚才备份的数据
f、执行change master to…..
5、测试
a、主库上创建一张表,并插入和删除数据查看slave上的情况
10.1.1.14: mysql> create table t (id int(11) not null auto_increment,name varchar(32) not null,primary key (id)); mysql> insert into t(name) select 'andy'; mysql> insert into t(name) select 'isadba'; mysql> select * from t; +----+--------+ | id | name | +----+--------+ | 1 | andy | | 2 | isadba | +----+--------+ 2 rows in set (0.00 sec) 10.1.1.15: mysql> use test Database changed mysql> select * from t; +----+--------+ | id | name | +----+--------+ | 1 | andy | | 2 | isadba | +----+--------+ 看来复制时正常的,现在我们在主库上删除isadba行。 10.1.1.14: mysql> delete from t where name='isadba'; Query OK, 1 row affected (0.00 sec) mysql> select * from t; +----+------+ | id | name | +----+------+ | 1 | andy | +----+------+ 10.1.1.15: mysql> select * from t; +----+------+ | id | name | +----+------+ | 1 | andy | +----+------+
b、从复制失败中恢复复制 我们先在slave中插入一条记录,然后在master上也出入一条记录,就会出现复制最常见的问题。 10.1.1.15: insert into t (name) select 'mysql'; 现在还是一切正常,等我们master也插入相行后,slave就会报错了。 10.1.1.14: mysql> insert into t(name) select 'mysql_master'; Query OK, 1 row affected (0.00 sec) 10.1.1.15: mysql> show slave status\G Slave_IO_Running: Yes Slave_SQL_Running: No Last_Errno: 1062 Last_Error: Error 'Duplicate entry '3' for key 'PRIMARY'' on query. Default database: 'test'. Query: 'insert into t(name) select 'mysql_master'' 这就是最容易出现的主键冲突,现在我们来解决这个问题: 解决分两种,一种是跳过错误,让这个错误存在,一种是完整的修复,让数据一致。 在slave使用 set global sql_slave_skip_counter=1; start slave; //这样可以跳过错误,现在复制恢复正常了。但是master和slave的数据却不一样了。 10.1.1.14: mysql> select * from t; +----+--------------+ | id | name | +----+--------------+ | 1 | andy | | 3 | mysql_master | +----+--------------+ 10.1.1.15: mysql> select * from t; +----+-------+ | id | name | +----+-------+ | 1 | andy | | 3 | mysql | +----+-------+
我们需要删除不同的这条记录,保证主从一致,在主库上执行如下操作,如果是RBR的日志,可能slave还会报错,不过没关系,那就在客户端也删除id是3那条记录,
并且掉过复制报错就行了。上面有一个Last_Errno: 1062的行,我们可以把1062这个错误号加到配置文件中(slave_skip_errors),让复制发现这种错误自动跳过。如果数据一致性要求高
的话,我不推荐使用此选项,应该成程序根源上找问题和方法解决。
10.1.1.14: mysql> delete from t where id=3; Query OK, 1 row affected (0.00 sec)
到此mysql M/S复制基本搭建完成。
上面M/S架构已经搭建完成了,现在我们要做一些修改把M/S配置改变成M/M架构。
1、修改配置文件
主要解决
a、自增长冲突的问题
b、关闭其中一个服务的写入
检查配置文件是否有以下内容,并且保证每个值都没有重复定义
10.1.1.14: log-bin=mysql-bin binlog_format=mixed server-id=1 auto_increment_increment=2 auto_increment_offset=1 skip_slave_start log_slave_updates 10.1.1.15: log-bin=mysql-bin binlog_format=mixed server-id=2 auto_increment_increment=2 auto_increment_offset=2 skip_slave_start log_slave_updates read_only
更新配置文件以后,从新启动服务器。
2、在10.1.15上授权允许10.1.1.14复制。10.1.1.15: mysql>start slave; mysql> grant replication slave,file on *.* to 'slave'@'10.1.1.14' identified by 'slave'; Query OK, 0 rows affected (0.01 sec) mysql> flush privileges; Query OK, 0 rows affected (0.01 sec)
3、10.1.1.15上锁表,并在10.1.14上配置slave,这个时候尽量停止业务,避免数据不一致。
10.1.1.15: mysql> flush tables with read lock; Query OK, 0 rows affected (0.01 sec) flush tables with read lock mysql> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000006 | 347 | | | +------------------+----------+--------------+------------------+ 1 row in set (0.00 sec) 10.1.1.14: mysql> CHANGE MASTER TO -> MASTER_HOST='10.1.1.15', -> MASTER_USER='slave', -> MASTER_PASSWORD='slave', -> MASTER_PORT=9188,MASTER_LOG_FILE='mysql-bin.000006', -> MASTER_LOG_POS=347, -> MASTER_CONNECT_RETRY=10; Query OK, 0 rows affected (0.00 sec) mysql> start slave; Query OK, 0 rows affected (0.00 sec) mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.1.1.15 Master_User: slave Master_Port: 9188 Connect_Retry: 10 Master_Log_File: mysql-bin.000006 Read_Master_Log_Pos: 347 Relay_Log_File: zj13-relay-bin.000002 Relay_Log_Pos: 253 Relay_Master_Log_File: mysql-bin.000006 Slave_IO_Running: Yes Slave_SQL_Running: Yes 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: 347 Relay_Log_Space: 408 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: 2 1 row in set (0.00 sec) 10.1.1.15: mysql> unlock tables; Query OK, 0 rows affected (0.00 sec) mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 10.1.1.14 Master_User: slave Master_Port: 9188 Connect_Retry: 10 Master_Log_File: mysql-bin.000004 Read_Master_Log_Pos: 107 Relay_Log_File: zj14-relay-bin.000005 Relay_Log_Pos: 253 Relay_Master_Log_File: mysql-bin.000004 Slave_IO_Running: Yes Slave_SQL_Running: Yes 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: 107 Relay_Log_Space: 554 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: 1 1 row in set (0.00 sec)
4、测试,分别在两个分别插入一条数据,然后看两个服务器数据是否相同。一般生产环境下,写入点只有一个,因为两个写入点很容易出现数据不同步。