mysql-主从同步

阅读量: zyh 2015-03-29 12:33:11
Categories: > Tags:

基本原理

mysql的主从复制默认基于异步复制,主要有三个进程参与,分别是主mysql上mysqldump进程,从mysql的i/o进程和sql进程。

执行过程:

  1. 从mysql的I/O进程向主mysql上mysqldump进程发起请求,读取主服务的本地binlog,主服务将数据通过3306端口发送给从mysql。
  2. 从mysql的I/O进程将数据存放到中继日志中,同时在master-info文件中记录所获取的binlog数据对应的日志文件名和日志读取点位置,便于告知主mysql下次发送二进制日志数据的起始点。
  3. 从mysql的sql进程检测到中继日志有数据后,在从mysql本地执行新加的语句,并将执行的语句的结果,存入数据库。

优点

MySQL的主服务器上的I/O线程,将变更数据按照事务提交的顺序写到binlong中后,就直接返回给客户端数据更新成功,主服务不会考虑从服务是否发起同步请求。所以不管从同步还是不同步,都不会影响到主库的数据写入。

缺点

如果主挂了,主的二进制日志还没来得及发给从,又没有其它措施保存二进制日志,那数据就丢了。

搭建步骤

master

  1. 创建监控主从状态的用户
grant replication slave,replication client on *.* to myreplic@'<slave_ip>' identified by '123456';
flush privileges;

💥做主从服务器的原则是,MYSQL版本要相同,如果不能满足,最起码从服务器的MYSQL的版本必须高于主服务器的MYSQL版本

  1. 主关键配置
[mysqld] 
server-id=100
log-bin=mysql-bin 
binlog-ignore-db=mysql
binlog_format=row
binlog_row_image=FULL
max_allowed_packet=128M

💥最好是只设置binlog-ignore-db。在5.5的版本中,同时设置了binlog-do-db和binlog-ignore-db会导致日志不再记录。

binlog-do-db可以只记录某个库的二进制日志。

  1. 锁表、备份、二进制日志信息

导出包括锁表语句以及master的二进制日志语句,均可以用下列一条命令处理

mysqldump --all-databases --lock-all-tables --master-data=2 -p<密码> > slave.sql

slave

  1. 从关键配置
server-id=200
replicate-ignore-db=mysql
relay-log=relay-bin
relay_log_index=relay-bin.index
max_allowed_packet=128M
  1. 导入全量数据
mysql –u root –p<密码> < slave.sql
  1. 链接主,并告知主从哪开始发日志数据

语句可以从 slave.sql 里获取

mysql> change master to MASTER_HOST='<master_ip>', MASTER_USER='myreplic', MASTER_PASSWORD='123456', MASTER_LOG_FILE='master-bin.xxxxx', MASTER_LOG_POS=xxx;

查看主从服务器的状态

mysql> show slave status\G;
                  Master_Host: 10.200.10.10         -- master服务器IP
                  Master_User: myreplic                 -- 主从同步用户
                  Master_Port: 3306                 -- 主库端口
                Connect_Retry: 60                   -- 建立连接尝试次数
              Master_Log_File: mysql-bin.000010     -- master的binlog日志,在master上执行show master status查看
          Read_Master_Log_Pos: 33101249             -- 中继器读到的master binlog的位置
               Relay_Log_File: relay-bin.000010     -- 从库中继器的日志文件
                Relay_Log_Pos: 367                  -- 中继日志读到的位置
        Relay_Master_Log_File: mysql-bin.000010     -- 当前中继读到的master的binlog文件
             Slave_IO_Running: Yes  -- I/O线程状态
            Slave_SQL_Running: Yes  -- SQL线程状态
              Replicate_Do_DB: 
          Replicate_Ignore_DB: mysql,information_schema,performance_schema,sys -- 主从同步忽略的库
           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: 33101249              -- SQL线程执行到的Relay_Master_Log_File文件的位置
              Relay_Log_Space: 741
              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                        -- master库的serverId,在my.cnf中配置的,主从不能设置一样
                  Master_UUID: 
             Master_Info_File: mysql.slave_master_info  -- master的信息,这里根据master_info_repository配置决定
                    SQL_Delay: 0
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates
           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
         Replicate_Rewrite_DB: 
                 Channel_Name: 
           Master_TLS_Version:

半同步模式

原理

半同步模式的出现是为了解决异步复制的丢数据问题。半同步模式分两种AFTER_COMMITAFTER_SYNC

AFTER_COMMIT模式下:

主服务器提交事务之后,并不会返回OK给客户端,而是等待从服务器发送中继日志接收完毕的ACK确认。

因事务已提交,所以其它客户端可以看到事务信息。

如果从服务器接收中继日志期间,主服务器挂了,则故障转移后,客户端在老主和新主上看到的信息就不一致了。

5.7.2版本之后的【AFTER_SYNC】大概原理:可以保证的是不丢数据,不过会多数据

就是在【主】将事务写入二进制日志之后,就会等待【从】接收二进制日志,在【从】将日志写入到中继日志后并返回【ACK】给【主】,主才会将事务提交给存储引擎执行并返回ok给客户端。

那么,当【主】挂的时候,且客户端没有收到【主】响应,则有两种情况:

  1. 若【从】还未接收二进制日志到中继日志,那么故障转移后,【旧主】还未提交事务 + 【新主(旧从)】中继日志里没有事务,所以客户端在【新主(旧从)】上重新发起提交事务,此时【新主】数据既没有少,也没有多。
  2. 若【从】已经接收二进制日志中继日志,那么故障转移后,【旧主】还未提交事务 + 【新主(旧从)】中继日志里有事务,所以客户端在【新主(旧从)】上重新发起提交事务,此时【新主(旧从)】数据会出现重复。

配置方法

# 主服务器
安装插件:mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
启动模块:mysql> SET GLOBAL rpl_semi_sync_master_enabled = 1;
# 从服务器
安装插件:msyql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
启动模块:mysql> SET GLOBAL rpl_semi_sync_slave_enabled = 1;
重启从的IO线程: mysql> STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;
# On Master
[mysqld]
plugin-load="rpl_semi_sync_master=semisync_master.so"
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=10000   # 此单位是毫秒,主10秒内没有收到从的ACK,就会降级为异步复制
rpl_semi_sync_master_wait_for_slave_count=1 # 主在rpl_semi_sync_master_timeout内只需要收到一个从的ACK,就可以返回响应给客户端。若没达成条件,就会转为异步。
rpl_semi_sync_master_wait_no_slave=ON # ON表示仅当事务期间,Rpl_semi_sync_master_clients < rpl_semi_sync_master_wait_for_slave_count ,才会转为异步。OFF表示,哪怕空闲的时候,也会转为异步。
rpl_semi_sync_master_wait_point=AFTER_SYNC # 主等待从同步完事务后才提交事务到存储引擎

# On Slave
[mysqld]
plugin-load="rpl_semi_sync_slave=semisync_slave.so"
rpl_semi_sync_slave_enabled=1

✨rpl_semi_sync_master_wait_point 参数若设置为 AFTER_COMMIT,则表示主提交事务到存储引擎后才会同步事务到从。

配置参数

mysql> show variables like '%Rpl%';

状态参数

半同步模式开启后,通过主可以看到半同步的一些状态

msyql> show status like '%Rpl_semi%';
+--------------------------------------------+-------+
| 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    | # OFF表示出现异常转为异步模式
| 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)

同步错误

当同步出错的时候,你可以通过show slave status\G看到错误信息,例如:

1032:记录不存在,当更新或删除一个条不存在的记录时,我们可以忽略此异常
1050:数据表已经存在

上述几个错误,根据情况,一般都可以直接通过slave-skip-errors=<错误码>跳过错误,从而恢复同步。

1236: Got fatal error 1236 from master when reading data from binary log: 'Client requested master to startreplicationfromposition > filesize'
主库异常断电,导致buffer中的事务来不及写入二进制日志,使从库I/O线程读取到比二进制日志中end pos还大的pos。

通过mysqlbinlog查看Relay_Master_Log_File文件,找到对应Exec_Master_Log_Pos相近的pos.

这里有两种情况:

一种是有相近的pos值,然后从库通过执行select master_pos_wait方式,查看已经同步的pos值,找到未同步的,然后从库执行change master to master_log_pos=xxx;
另外一种情况就是Relay_Master_Log_File没有相近的pos值,这时候一般是本binlog已经完成同步,将master_log_file指向下一个文件,pos值改成下个文件的最小值即可。

1236: Got fatal error 1236 from master when reading data from binary log

需要重建slave。常见于主库二进制日志被提前删除。加大expire_logs_days的时间。