一、MySQL的复制是将主数据库(master)的数据复制到从(slave)数据库上,专业一点讲就是将主数据库DDL和DML操作的二进制日志传到从库上,然后从库对这些二进制日志进行重做,使得主数据库与从数据库的数据保持同步。
二、MySQL复制的一些优点:
- 可以将大部分的查询任务放在从库上进行,降低主库的负载,提高性能,但要注意实时性要求高的数据仍需在主库上读取。
- 如果主库出现了宕机,可以快速切换到从库上,提高了可用性。
- 可以在从库上进行数据备份,降低在备份期间对主库的影响。
- 将数据挖掘和分析等工作放在从库上进行,可以降低对主库的性能影响。
三、MySQL复制处理数据的三种模式:
- 基于语句复制:也称为SBR(Statement Based Replication),基于实际执行的SQL语句来进行复制的方案,BINLOG_FORMAT=STATEMENT。
- 基于行的复制:也叫RBR(Row Bases Replication),BINLOG_FORMAT=ROW。
- 混合复制模式:也成MBR,基于SQL语句复制和行的复制,BINLOG_FORMAT=MIXED。
查看二进制日志的格式:
mysql> show global variables like ‘binlog_format‘; +---------------+-----------+ | Variable_name | Value | ---value的值有三种:statement,row,mixed +---------------+-----------+ | binlog_format | STATEMENT | +---------------+-----------+ 1 row in set (0.00 sec)
设置二进制日志的格式:
mysql> set global binlog_format=‘row‘; --设置全局binlog_format,可以使用set session binlog_format=‘row‘来设置当前会话的binlog_format Query OK, 0 rows affected (0.00 sec) mysql> show GLOBAL variables like ‘binlog_format‘; +---------------+-------+ | Variable_name | Value | +---------------+-------+ | binlog_format | ROW | +---------------+-------+ 1 row in set (0.00 sec)
在SQL中查看日志文件中的事件:
mysql> show binlog events in ‘mysql-bin.000027‘ from 107; --from指定从日志哪个位置开始 +------------------+-----+------------+-----------+-------------+---------------------------------------------+ | Log_name | Pos | Event_type | Server_id | End_log_pos | Info | +------------------+-----+------------+-----------+-------------+---------------------------------------------+ | mysql-bin.000027 | 107 | Query | 3306 | 175 | BEGIN | | mysql-bin.000027 | 175 | Query | 3306 | 264 | use `test`; insert into t1 values (10) | | mysql-bin.000027 | 264 | Query | 3306 | 333 | COMMIT | | mysql-bin.000027 | 333 | Query | 3306 | 401 | BEGIN | | mysql-bin.000027 | 401 | Query | 3306 | 495 | use `test`; update t1 set id=70 where id=60 | | mysql-bin.000027 | 495 | Query | 3306 | 564 | COMMIT | | mysql-bin.000027 | 564 | Stop | 3306 | 583 | | +------------------+-----+------------+-----------+-------------+---------------------------------------------+ 7 rows in set (0.00 sec)
基于SQL语句的二进制日志记录原始SQL操作,而基于ROW格式的二进制日志记录的是每行数据的变更,采用64位编码,使用mysqlbinlog查看时须搭配--base64-output=‘decode-rows‘ --vv参数来解码查看。
四、MySQL复制流程
MySQL复制基于二进制日志,开启二进制日志功能是必须的。当从库启动复制时(start slave),首先创建一个I/O线程连接主库,主库接着创建Binlog Dump线程读取二进制日志事件发送给从库的I/O线程,I/O线程获取数据后更新从库的中继日志Relay Log,然后从库的SQL线程读取中继日志中更新的数据库事件并应用,如下图所示:
可以使用SHOW PROCESSLIST命令在主库和从库上分别执行,查看主库BINLOG DUMP线程和从库SQL线程状态:
----------------------在主库上------------- mysql> show processlist\G *************************** 3. row *************************** Id: 18 User: repl Host: localhost:2275 db: NULL Command: Binlog Dump Time: 76 State: Master has sent all binlog to slave; waiting for binlog to be updated Info: NULL 3 rows in set (0.00 sec) --------------------在从库上--------------- *************************** 2. row *************************** Id: 27 User: system user Host: db: NULL Command: Connect Time: 301 State: Waiting for master to send event Info: NULL *************************** 3. row *************************** Id: 28 User: system user Host: db: NULL Command: Connect Time: 246703 State: Slave has read all relay log; waiting for the slave I/O thread to update it Info: NULL
五、搭建复制环境
由于本人苦逼学生一枚,只有一台电脑,所以只能在一台电脑上开启多个MySQL服务,开启过程如下所示:
1.停止mysql服务
2.copy mysql安装目录到指定目录E:\,这里随意,不要和原目录重叠即可
3.copy mysql data数据文件到E:\data
4.copy一份my.ini文件到上面指定的目录下
5.修改复制后的配置文件my.ini,内容如下:
[client]
port=3307 #第一个数据库的默认端口是3306 这里需要另外启用一个端口
# The TCP/IP Port the MySQL Server will listen on
port=3307
# Path to installation directory. All paths are usually resolved relative to this.
basedir="E:\MySQL\MySQL Server 5.5\" #第二个数据库basedir
# Path to the database root
datadir="E:\MySQL\MySQL Server 5.5\data\" #第二个数据库datadir
6.创建新的mysql服务
mysqld install MySQLSLAVE --defaults-file="E:\MySQL\MySQL Server 5.5\my.ini"
7.修改注册表,如下:
KEY_LOCAL_MACHINE-->SYSTEM-->CurrentControlSet-->Services
找到刚才创建的MySQLSLAVE,将ImagePath修改成如下":
"E:\MySQL\MySQL Server 5.5\bin\mysqld" --defaults-file="E:\MySQL\data\my.ini" mysqlsalve
复制环境搭建过程如下:
1.修改主数据库的配置文件my.ini,修改内容如下:
server-id=3306 #主从复制是通过二进制文件来进行,所以要开启日志功能 log-bin=mysql-bin #主机,读写都可以 read-only=0 #需要备份数据,多个写多行 binlog-do-db=test #不需要备份的数据库,多个写多行 binlog-ignore-db=mysql
2.修改从数据库的配置文件my.ini,修改内容如下:
#主从配置 server-id=3307 log-bin=mysql-bin #如果从服务器发现主服务器断掉,重新连接的时间差(秒) #master-connect-retry=60 #只复制某个库 replicate-do-db=test #不复制某个库 replicate-ignore-db=mysql
3.保持主从的test库初始状态一致
mysql> flush tables with read lock; Query OK, 0 rows affected (0.05 sec) ---首先锁定表为读有效,防止数据库有更新操作,然后利用COPY/CP命令将数据文件目录复制到从库数据目录下 ---或者直接关闭mysql服务,手动复制数据文件目录到指定目录下---拷贝完后,恢复写操作
mysql> unlock tables;
Query OK, 0 rows affected (0.00 sec)
4.在主库上创建一个专门用来复制的用户repl
mysql> GRANT REPLICATION SLAVE ON *.* TO ‘REPL‘@‘127.0.0.1‘ IDENTIFIED BY ‘1234567‘; Query OK, 0 rows affected (0.31 sec)
5.重启主库,然后执行show master status,记下file和position字段对应的参数
mysql> show master status; +------------------+----------+--------------+------------------+ | File | Position | Binlog_Do_DB | Binlog_Ignore_DB | +------------------+----------+--------------+------------------+ | mysql-bin.000031 | 262 | test | mysql | +------------------+----------+--------------+------------------+ 1 row in set (0.00 sec)
6、在从库设置它的master:
mysql> change master to master_host=‘127.0.0.1‘,master_port=3306,master_user=‘repl‘,master_password=‘asdf‘,master_log_file=‘mysql-bin.000031‘,master_log_pos=262; Query OK, 0 rows affected (0.19 sec) ----这里的master_log_file和master_log_pos对应刚才show master status记下的参数。
7.在从库上开启复制start slave
mysql> start slave; Query OK, 0 rows affected, 1 warning (0.00 sec) --这里有个warning,是因为我已经开启了slave
8.验证复制搭建是否成功
mysql> show processlist\G; *************************** 2. row *************************** Id: 27 User: system user Host: db: NULL Command: Connect Time: 6349 State: Waiting for master to send event Info: NULL *************************** 3. row *************************** Id: 28 User: system user Host: db: NULL Command: Connect Time: 580 State: Slave has read all relay log; waiting for the slave I/O thread to update it Info: NULL 3 rows in set (0.00 sec) ----以上信息表名slave已经连上了master,并开始接收和执行日志---
9.使用show slave status来查看slave信息
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 127.0.0.1 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000031 Read_Master_Log_Pos: 262 Relay_Log_File: Lenovo-PC-relay-bin.000059 Relay_Log_Pos: 408 Relay_Master_Log_File: mysql-bin.000031 Slave_IO_Running: Yes --I/O线程已开启 Slave_SQL_Running: Yes --SQL线程也开启 Replicate_Do_DB: test 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: 262 Relay_Log_Space: 714 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 --I/O没有错误 Last_IO_Error: Last_SQL_Errno: 0 --sql应用也没有错误 Last_SQL_Error: Replicate_Ignore_Server_Ids: Master_Server_Id: 3306 1 row in set (0.00 sec)
10.验证复制的正确性
-------在主库上的test库创建表cc------ mysql> create table cc (id int not null); Query OK, 0 rows affected (0.10 sec) mysql> insert into cc values (10),(20); Query OK, 2 rows affected (0.00 sec) Records: 2 Duplicates: 0 Warnings: 0 -------在从库上查看是否存在表cc----- mysql> show tables like ‘cc‘; +---------------------+ | Tables_in_test (cc) | +---------------------+ | cc | +---------------------+ 1 row in set (0.00 sec) mysql> select * from cc; +----+ | id | +----+ | 10 | | 20 | +----+ 2 rows in set (0.00 sec) ------------说明复制搭建已经成功------
六、半同步复制
MySQL复制默认采用异步的同步机制,主库和从库的数据之间存在一定的延时,不能保证数据的一致性和及时性。因此有必要开启半同步复制,而半同步复制模式是由mysql插件来实现,主从库使用不同的插件(semisync_master.so/semisync_slave.so),安装插件如下所示。
检查mysql服务是否支持动态加载插件
mysql> show variables like ‘%dynamic_loading%‘; +----------------------+-------+ | Variable_name | Value | +----------------------+-------+ | have_dynamic_loading | YES | +----------------------+-------+ 1 row in set (0.00 sec) ----也可以直接执行select @@have_dynamic_loading
安装插件
mysql> install plugin rpl_semi_sync_master soname ‘semisync_master.dll‘; ERROR 1125 (HY000): Function ‘rpl_semi_sync_master‘ already exists --我已经安装了该插件
查看插件安装是否成功
mysql> select * from mysql.plugin; +----------------------+---------------------+ | name | dl | +----------------------+---------------------+ | rpl_semi_sync_master | semisync_master.dll | +----------------------+---------------------+ 1 row in set (0.03 sec)
从库也使用同样的方法安装semisync_slave插件
mysql> select * from mysql.plugin; +---------------------+--------------------+ | name | dl | +---------------------+--------------------+ | rpl_semi_sync_slave | semisync_slave.dll | +---------------------+--------------------+ 1 row in set (0.00 sec)
打开半同步复制,因为mysql默认是关闭的
mysql> show variables like ‘%semi_sync%‘; +------------------------------------+-------+ | Variable_name | Value | +------------------------------------+-------+ | rpl_semi_sync_master_enabled | OFF | | rpl_semi_sync_master_timeout | 10000 | | rpl_semi_sync_master_trace_level | 32 | | rpl_semi_sync_master_wait_no_slave | ON | +------------------------------------+-------+ ---------主库------ mysql> set global rpl_semi_sync_master_enabled=on; Query OK, 0 rows affected (0.01 sec) ---------从库------------- mysql> set global rpl_semi_sync_slave_enabled=on; Query OK, 0 rows affected (0.00 sec) ----重启I/O线程 mysql> stop slave io_thread; Query OK, 0 rows affected (0.01 sec) mysql> start slave io_thread; Query OK, 0 rows affected (0.00 sec)
半同步复制配置完毕后,查看半同步复制的状态信息,在主库上执行show global status like ‘%semi_sync%‘;
mysql> show global status like ‘%semi_sync%‘; +--------------------------------------------+-------+ | Variable_name | Value | +--------------------------------------------+-------+ | Rpl_semi_sync_master_clients | 1 | | Rpl_semi_sync_master_net_avg_wait_time | 0 | | Rpl_semi_sync_master_net_wait_time | 0 | | Rpl_semi_sync_master_net_waits | 0 | | Rpl_semi_sync_master_no_times | 0 | | Rpl_semi_sync_master_no_tx | 0 | --表示不是通过半同步复制及时响应的事务数 | Rpl_semi_sync_master_status | ON | --表示当前半同步复制已经打开 | Rpl_semi_sync_master_timefunc_failures | 0 | | Rpl_semi_sync_master_tx_avg_wait_time | 0 | | Rpl_semi_sync_master_tx_wait_time | 0 | | Rpl_semi_sync_master_tx_waits | 0 | | Rpl_semi_sync_master_wait_pos_backtraverse | 0 | | Rpl_semi_sync_master_wait_sessions | 0 | | Rpl_semi_sync_master_yes_tx | 0 | --表示通过半同步模式复制到从库的事务数 +--------------------------------------------+-------+ 14 rows in set (0.00 sec)
半同步同步还有一个超时时间,超过了这个时间,主库将关闭半同步复制模式,改为异步复制。如果这时从库重新连接主库,主库将自动切换到半同步复制模式。查看超时时间
mysql> show variables like ‘%semi%time%‘; +------------------------------+-------+ | Variable_name | Value | +------------------------------+-------+ | rpl_semi_sync_master_timeout | 10000 | --10s +------------------------------+-------+ 1 row in set (0.00 sec)
七、MySQL复制主要启动参数
1.master_host,master_port,master_user,master_password这些参数主要用来记录需要复制的主库的地址,端口,用户,密码等信息,就不具体介绍了。
2.log_slave_updates:用来指定从库的更新操作是否记录到二进制日志中去,如果要搭建的是主-主复制,则该参数必须指定为ON(set global log_slave_updates=on或在配置文件中永久启动)
3.read-only:用来限制普通用户对从库的更新操作,只接收超级用户的更新操作。
4.master_connect_retry:指定当和主库丢失连接时重试的时间间隔,默认为60。
5.replicate_do_db,replicate_ignore_db,replicate_do_table,replicate_ignore_table:这些参数用来指定复制的数据库或表,比如指定复制的表为replicate_do_table=test.cc,其他的表则不会复制。
6.slave_skip_errors:用来指定从库复制中可以跳过的错误号或跳过全部错误,slave_skip_errors=[err_code1,err_code2...|all]
7.master_delay:用来指定延时复制的时间隔间
八、复制的管理维护
1.查看从库状态
mysql> show slave status\G *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 127.0.0.1 Master_User: repl Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysql-bin.000031 Read_Master_Log_Pos: 736 Relay_Log_File: Lenovo-PC-relay-bin.000060 Relay_Log_Pos: 442 Relay_Master_Log_File: mysql-bin.000031 Slave_IO_Running: Yes Slave_SQL_Running: Yes Replicate_Do_DB: test Replicate_Ignore_DB: mysql Replicate_Do_Table: Replicate_Ignore_Table: Replicate_Wild_Do_Table: Replicate_Wild_Ignore_Table:----截取部分信息
2.主库和从库同步
-----在主库上执行----- mysql> flush tables with read lock; Query OK, 0 rows affected (0.00 sec) -----在从库上执行----- mysql> select master_pos_wait(‘mysql-bin.000031‘,‘ 736‘); +--------------------------------------------+ | master_pos_wait(‘mysql-bin.000031‘,‘ 736‘) | +--------------------------------------------+ | 0 | +--------------------------------------------+ 1 row in set (0.03 sec) --master_pos_wait的参数值可以在主库执行show master status得到 --该select语句会阻塞到从库达到指定的日志文件的偏移量后,返回0,表示与主库同步了 --完了之后在主库上执行unlock tables
3.从库复制出现错误
在从库应用中继日志时,可能会出现一些错误,例如表结构不对,函数不存在等,如果是不太重要的错误,则可使用sql_slave_skip_counter变量来忽略更新失败的语句。示例如下:
mysql> stop slave; Query OK, 0 rows affected (0.02 sec) mysql> set global sql_slave_skip_counter=1; ----如果是autoincrement或last_insert_id()则为2 mysql> start slave; Query OK, 0 rows affected (0.00 sec)
4.主-主复制时自增变量冲突的问题
主-主复制需要定制auto_increment_increnment和auto_increment_offset的值来避免重复冲突,这两个变量的值默认为1,这也是重复冲突的源头
mysql> show variables like ‘auto_increment_%‘; +--------------------------+-------+ | Variable_name | Value | +--------------------------+-------+ | auto_increment_increment | 1 | | auto_increment_offset | 1 | +--------------------------+-------+ 2 rows in set (0.00 sec)
采用以下的设置可以避免重复
master1:auto_increment_increment=2,auto_increment_offset=1 master1:auto_increment_increment=2,auto_increment_offset=0
5.提高复制性能
方案一:采用一主多从的复制架构,利用replicate_do_db,replicate_ignore_db等参数使得不同的从库复制不同的库/表,降低每个从库的写入压力。
方案二:利用5.6版本的多线程并发复制,通过设置变量slave_parallel_workers来实现。
stop slave set global slave_parallel_works=2; start slave; ---或者在my.ini中天加slave_parallel_workers=2
6.主从切换
1.在每个从库上执行stop slave io_thread,停止接收日志,接着执行show processlist,如果state字段值为Has read all relay log,表示更新都应用完毕。
2.在需要提升为主库的从库上执行stop slave,reset slave和reset master,将从库重置成主库。
3.其他的从库执行都执行stop slave,然后执行change master to master_host=新的主库地址。
4.所有应用指向新的主库。