MySQL学习笔记十七:复制特性

一、MySQL的复制是将主数据库(master)的数据复制到从(slave)数据库上,专业一点讲就是将主数据库DDL和DML操作的二进制日志传到从库上,然后从库对这些二进制日志进行重做,使得主数据库与从数据库的数据保持同步。

二、MySQL复制的一些优点:

  1. 可以将大部分的查询任务放在从库上进行,降低主库的负载,提高性能,但要注意实时性要求高的数据仍需在主库上读取。
  2. 如果主库出现了宕机,可以快速切换到从库上,提高了可用性。
  3. 可以在从库上进行数据备份,降低在备份期间对主库的影响。
  4. 将数据挖掘和分析等工作放在从库上进行,可以降低对主库的性能影响。

三、MySQL复制处理数据的三种模式:

  1. 基于语句复制:也称为SBR(Statement Based Replication),基于实际执行的SQL语句来进行复制的方案,BINLOG_FORMAT=STATEMENT。
  2. 基于行的复制:也叫RBR(Row Bases Replication),BINLOG_FORMAT=ROW。
  3. 混合复制模式:也成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 processlistG
*************************** 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:MySQLMySQL Server 5.5"         #第二个数据库basedir

  # Path to the database root

  datadir="E:MySQLMySQL Server 5.5data"    #第二个数据库datadir

  6.创建新的mysql服务

  mysqld install MySQLSLAVE  --defaults-file="E:MySQLMySQL Server 5.5my.ini"

  7.修改注册表,如下:

  KEY_LOCAL_MACHINE-->SYSTEM-->CurrentControlSet-->Services

  找到刚才创建的MySQLSLAVE,将ImagePath修改成如下":

  "E:MySQLMySQL Server 5.5inmysqld" --defaults-file="E:MySQLdatamy.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 processlistG;
*************************** 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 statusG
*************************** 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 statusG
*************************** 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.所有应用指向新的主库。

原文地址:https://www.cnblogs.com/zmxmumu/p/4467589.html