第1章 MySQL主从复制
1.1 数据库损坏了?
主要理解为:业务不能使用数据库
外在原因:
1、网络问题
2、业务应用有问题,客户端损坏
数据库本身的原因:
1、物理损坏:机器坏了、硬盘坏了、存储坏了
2、逻辑损坏:误drop、delete、truncate、、update。
解决方案:
1、备份
2、主从复制
1.2 MySQL主从复制
1.2.1 MySQL复制概念
指将主数据库的DDL和DML操作通过二进制日志传到复制服务器上,然后在复制服务器上将这些日志文件重新执行,从而使复制服务器和主服务器的数据保持同步。复制过程中一个服务器充当主服务器(master),而一个或多个其它服务器充当从服务器(slaves)。主服务器将更新重新写入二进制日志文件,并维护文件的一个索引以跟踪日志循环。这些日志可以记录发送到从服务器的更新。当一个从服务器连接主服务器时,它通知主服务器、从服务器在日志中读取的最后一次成功更新的位置。从服务器接受从那时起发生的任何更新,然后封锁并等待主服务器通知新的更新。
复制的用途:
通过主从复制(master-slave)的方式来同步数据,再通过读写分离(mysql-proxy)来提升数据库的并发负载能力,或者用来作为主备机的设计,保证在主机停止响应之后在很短的时间内就可以将应用切换到备机上继续运行。
优势:
(1)数据库集群系统具有多个数据库节点,在单个节点出现故障的情况下,其他正常节点可以继续提供服务。
(2)如果主服务器上出现了问题可以切换到从服务器上
(3)通过复制可以在从服务器上执行查询操作,降低了主服务器的访问压力,实现数据分布和负载均衡
(4)可以在从服务器上进行备份,以避免备份期间影响主服务器的服务。
1.2.2 主从复制简介
1、能做什么?
² 高可用
² 辅助备份
² 分担负载
2.主从是怎么实现的?
² 通过二进制日志
² 至少两台(主, 从)
² 主服务器的二进制日志”拿“到从服务器上再运行一遍。
² 通过网络连接两台机器,一般都会出现延迟的状态。也可以说是异步。
3.MySQL主从复制的企业应用场景
² 应用场景1:从服务器作为主服务器的实时数据备份
² 应用场景2:主从服务器实现读写分离,从服务器实现负载均衡
² 应用场景3:把多个从服务器根据业务重要性进行拆分访问
1.2.3 主从复制原理
1. 前提
² 主服务器一定要打开二进制日志
² 必须两台服务器(或者是多个实例)
² 从服务器需要一次数据初始化
² 如果主从服务器都是新搭建的话,可以不做初始化
² 如果主服务器已经运行很长时间了,可以通过备份将主库数据恢复到从库
² 主库必须要有对从库复制请求的用户。
² 从库需要有relay-log设置,存放从主库传过来的二进制日志
² 在第一次的时候,从库需要change master to 去连接主库
² change mater 信息需要存放到master.info中
² 从库通过relay-log.info记录了已经应用过的relaylog信息发现了主库发生了新的变化
² 在复制过程中涉及到的线程
n 从库会开启一个IO thread 负责连接主库,请求binlog,接收binlog并写入relay-log中。
n 从库会开启一个SQ thread 负责执行relay-log中的事件
n 主库会开启一个dump thread 复制响应从IO thread中请求
复制过程
复制原理
原理:第一次开启主从
1.从库通过change master to 语句连接主库,并且让从库知道,二进制日志的起点位置(file名 position号)
2.从库的IO和主库的dump线程建立连接
3.从库根据changemaster to 语句提供的file名和position号,IO线程向主库发起binlog请求
4.主库dump线程根据从库的请求,将本地binlog以events方式发给从库IO线程
5.从库IO线程接收binlog events,并存放到本地relay-log中,传送过来的信息,会记录到master.info中。
6.从库应用relay-log,并且把应用过来的记录到relay-log.info,默认情况下,已经应用过来饿的relay会自动被清理purge。
到此,一次主从复制就完成,一旦主从运行起来,就不需要手工执行changemaster to,因为信息都会被存放到master.info 中,其他的过程也是一样的。
注意:如果不是新搭建的主从,需要将原来的数据全备,进行同步到从库中,然后在开启change master to。
过程:
1.初始化数据,使用备份将主库数据恢复到从库。
2.主库开启binlog server id,从库开启server id 默认开启relay log,建议自己设置relay-log,防止从库突然修改主机名,relaylog会生成新的名字,原来的就找不到了
3.主库中创建复制用户
4.从库开启change change master to
5.开启slave
6.验证主从
1.1.1 配置主从
1.在主库中添加用户权限
grant replication slave on *.* to repl@'10.0.0.%' identified by '123456';
flush privileges;
初始化数据
2.mysqldump -uroot -p123456 -A -B -F --master-data=2 >/tmp/full.sql
scp /tmp/server.sql 10.0.0.53:/tmp
在从库中进行source恢复
3.进入二进制文件找position号
在主库中查看binlog起点
mysql> mysql> show master;
+----------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+----------------+----------+--------------+------------------+-------------------+
| log-bin.000013 | 120 | | | |
+----------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
[root@db02 ~]# vi /tmp/full.sql
-- CHANGE MASTER TO
MASTER_LOG_FILE='mysql-bin.000003', MASTER_LOG_POS=120;
4.进入从库开启主从复制
set sql_log_bin=0;
CHANGE MASTER TO
MASTER_HOST='10.0.0.52 ',
MASTER_USER='repl',
MASTER_PASSWORD='123456',
MASTER_PORT=3306,
MASTER_LOG_FILE='mysql-bin.000004',
MASTER_LOG_POS=120;
注意:也可以设置relay-bin的名称。
检查状态
start slave;
show slave statusG
成功的标志是:
Slave_IO_Running: Yes
Slave_SQL_Running:
Yes
查看状态
[root@db02 ~]# mysql -S
/data/3307/mysql.sock -e "show slave statusG"|egrep
"_Running|Behind_Master"|head -3
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
Seconds_Behind_Master: 0
[root@db02 ~]#mysql> show slave statusG #查看状态
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 10.0.0.52
Master_User: repl
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000005
Read_Master_Log_Pos: 544
Relay_Log_File: DB03-relay-bin.000002
Relay_Log_Pos: 754
Relay_Master_Log_File: mysql-bin.000005
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: 544
Relay_Log_Space: 957
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: 52
Master_UUID: 9cd29dbb-d003-11e7-a49d-000c29310c49
Master_Info_File: /application/mysql-5.6.36/data/master.info
SQL_Delay: 0
SQL_Remaining_Delay: NULL
Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it
Master_Retry_Count: 86400
Master_Bind:
Last_IO_Error_Timestamp:
Last_SQL_Error_Timestamp:
Master_SSL_Crl:
Master_SSL_Crlpath:
Retrieved_Gtid_Set: 9cd29dbb-d003-11e7-a49d-000c29310c49:1-2
Executed_Gtid_Set: 9cd29dbb-d003-11e7-a49d-000c29310c49:1-2,
e19e3a5b-d33f-11e7-b9b6-000c29af0efd:1-123
Auto_Position: 1
1 row in set (0.00 sec)
mysql>
出现故障:
1、原因:
² 主机没启动,或者宕机
² 网络通信问题
² 防火墙
² 复制用户密码(用户密码错误)
2、故障
Slave_IO_Running: Connecting
Slave_SQL_Running: Yes
或者
Slave_IO_Running: NO
Slave_SQL_Running: NO
原因:在change master to 的时候,填错了用户密码,端口
1.1.2 配置主从注意事项
Slave_*_Running:
Slave_IO_RunningI/O 线程正在运行、未运行还是正在运行但尚未连接到主服务器。可能值分别为Yes、No 或Connecting。
Slave_SQL_RunningSQL 线程当前正在运行、未运行,可能值分别为Yes、No
主服务器日志坐标:
Master_Log_File和Read_Master_Log_Pos标识主服务器二进制日志中I/O 线程已经传输的最近事件的坐标。
如果Master_Log_File和Read_Master_Log_Pos的值远远落后于主服务器上的那些值,这表示主服务器与从属服务器之间事件的网络传输可能存在延迟。
中继日志坐标:
Relay_Log_File和Relay_Log_Pos列标识从属服务器中继日志中SQL 线程已经执行的最近事件的坐标。这些坐标对应于Relay_Master_Log_File和Exec_Master_Log_Pos列标识的主服务器二进制日志中的坐标。
如果Relay_Master_Log_File和Exec_Master_Log_Pos列的输出远远落后于Master_Log_File和Read_Master_Log_Pos列(表示I/O 线程的坐标),这表示SQL 线程(而不是I/O 线程)中存在延迟。即,它表示复制日志事件快于执行这些事件。
Last_IO_Error、Last_SQL_Error:
+分别导致I/O 线程或SQL 线程停止的最新错误的错误消息。在正常复制过程中,这些字段是空的。如果发生错误并导致消息显示在以上任一字段中,则错误值也显示在错误日志中。
Last_IO_Errno、Last_SQL_Errno:
与分别导致I/O 线程或SQL 线程停止的最新错误关联的错误编号。在正常复制过程中,这些字段包含编号0。
Last_IO_Error_Timestamp、Last_SQL_Error_Timestamp:
分别导致I/O 线程或SQL 线程停止的最新错误的时间戳,格式为YYMMDD HH:MM:SS。在正常复制过程中,这些字段是空的。
1.1.3 复制过滤
主库方面:
白名单:只记录白名单中列出的库的二进制日志
binlog-do-db
黑名单:不记录黑名单列出的库的二进制日志
binlog-ignore-db
从库
白名单:只执行白名单中列出的库或者表的中继日志
--replicate-do-db=test
--replicate-do-table=test.t1
--replicate-wild-do-table=test.x*
黑名单:不执行黑名单中列出的库或者表的中继日志
--replicate-ignore-db
--replicate-ignore-table
--replicate-wild-ignore-table
1.2 错误实例
从库binlog落后于主库
Master_Log_File: log-bin.000014
Read_Master_Log_Pos: 120
从库的logbin比主库的logbin慢的原因:
网络问题
主库dump线程繁忙
从库IO线程繁忙
【扩展】
延时节点概念:是SQL线程延时,不是IO线程延时。
SQL线程报错
原因:
² 主库操作对象在从库中不存在
² 主库操作对象的属性和从库不一致
² 主从操作顺序颠倒
解决方法:
跳过错误
stop slave;
set global sql_slave_skip_counter = 1;
start slave;
也可以在配置文件中跳过错误号码:
[mysqld]
slave-skip-errors = 1032,1062,1007
1.3 企业实例
背景:标准主从复制结构,在业务逻辑中有oldboy数据库,oldboy数据库下有t1表为生产表。
故障原因:开发人员在从库创建了一个oldgirl库,觉得不对,后又在主库中做了相同的操作。导致了从库复制失效。
解决方案:
主从复制故障及解决
stop slave; #<==临时停止同步开关。
set global sql_slave_skip_counter= 1 ; #<==将同步指针向下移动一个,如果多次不同步,可以重复操作。
start slave;
/etc/my.cnf
slave-skip-errors = 1032,1062,1007
如何避免问题?
从库设置为只读库
在my.cnf中添加read_only=1
单独在从库创建一个只读用户
在主库创建写用户
优点:
配置时不需要重启
故障切换时也不需要重启
1.4 主从架构演变
1.4.1 演变
备份
- 相当于实时备份
- 使用从库备份
问题:
如果从库只是作为备份服务器使用,那么主库的压力会增加,因为所有的业务都在主库进行读写(dump线程读取并发送给binlog)
解决方法:
- 一主一从
分出部分读业务到从库(读写分离) - 一主多从,分担压力(针对读业务多的需求)
但是这种一主多从的模式会使dump线程压力更大了 - 多级主从
使用中间库分担主库dump线程读取分发binlog的压力,由于中间库只作为分发者,不需要其他操作,为了提高中间库的性能,可以使用blackhole存储引擎。 - 双主模型
- 环状复制
1.4.2 高级应用架构
性能
² 读写分离——MySQLproxy、amoeba、xx-dbproxy等。
² 分库分表——cobar、自主研发等。
² 比较依赖于业务
² 实施思路:
² 判断语句类型
² 根据语句类型进行分发
² 负载均衡,分发到从库
² 会话持续性(减少用户认证之类的操作)
² 判断语句是否执行过(提高性能,减少重复操作)
高可用
² MMM架构——mysql-mmm(google)(不在使用)
² MHA架构——mysql-master-ha(日本DeNa)
² MGR ——5.7 新特性MySQLGroup replication
² PXC、MySQLCluster架构
1.4.3 多级主从部署(级联主从)
类似于一主一从的部署
不同之处在于主从之间多了一个中间服务器
[mysqld]
basedir = /application/mysql/
datadir = /application/mysql/data/
socket = /application/mysql/tmp/mysql.sock
character_set_server=utf8
sql_mode=NO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES
server-id = 2
log-bin=/tmp/log-bin
binlog-format=row
autocommit=1
log-slave-updates
[client]
socket = /application/mysql/tmp/mysql.sock
在中间服务器的my.cnf文件中需要开启binlog并添加log-slave-updates参数,表示强制刷新binlog,否则binlog日志不会刷新。
相当于做了两套主从。
reset slave;重置slave(关闭状态)