原理:
写在主库的操作,从库会同步其binlog日志,将操作应用到从库上。
从库设置成readonly 只读的,可以防止出现主从双写导致主从数据不一致。
因为 readonly 设置对超级 (super) 权限用户是无效的,而用于同 步更新的线程,就拥有超级权限,不影响从库同步主库的操作。
备库 B 跟主库 A 之间维持了一个长连接。主库 A 内部有一个线程,专门用于服务备库 B 的这个长连接。
流程如下:
1.在备库 B 上通过 change master 命令,设置主库 A 的 IP、端口、用户名、密码,以及 要从哪个位置开始请求 binlog,这个位置包含文件名和日志偏移量。
2.在备库 B 上执行 start slave 命令,这时候备库会启动两个线程, io_thread 和 sql_thread。其中 io_thread 负责与主库建立连接。
3.主库 A 校验完用户名、密码后,开始按照备库 B 传过来的位置,从本地读取 binlog, 发给 B。
4.备库 B 拿到 binlog 后,写到本地文件,称为中转日志(relay log)。
5.sql_thread 读取中转日志,解析出日志里的命令,并执行。
binlog的格式:
binlog 有两种格式,
statement格式:记录到 binlog 里的是语句原文
row格式:
- Table_map event,用于说明接下来要操作的表
- xx event,用于定义行为
里面会记录变动的所有信息,binlog_row_image 的默认配置是 FULL。
比如说 Delete_event 里面,包含了删掉的行的 所有字段的值。
如果把 binlog_row_image 设置为 MINIMAL,则只会记录必要信息,比如id。
3.Xid event 记录提交的事务id 表示事务已经提交
mixed格式:
其实它就是前两种格式 的混合。
因为row模式需要记录的数据太多, statement模式存在风险,比如说delete xx where id>10 and age<18 limit 2 。这个语句删除哪两条 取决于索引走的是哪个,如果主库是从id开始搜索,而从库是从age开始搜索,那么 主从就不一定是删的同样的数据。
mixed模式就是,如果没有风险的语句 就用statement ,如果有风险的 就用row。
将binlog设置成row模式,对恢复数据有好处,如果不小心用了delete命令,那么在binlog里面记录了delete前后的数据,所以可以用binlog将数据恢复回来。
27.mysql如何保障高可用
正常情况下,只要主库执行更新生成的所有 binlog,都可以传到备库并被正确地执行,备 库就能达到跟主库一致的状态,这就是最终一致性。
MySQL 要提供高可用能力,只有最终一致性是不够的
主备延迟
主备切换可能是一个主动运维动作,比如软件升级、主库所在机器按计划下线等,也可能是
被动操作,比如主库所在机器掉电。
主从同步的流程:
1.主库完成一个事务,写入binlog,时刻t1。
2.备库接收到binlog,时刻t2。
3.备库执行这个binlog,完成事务,时刻t3。
t3-t1就是延迟时间。
在备库中执行show slave status ,显示seconds_behind_master 可以看到备库和主库的延迟时间,单位是秒。
binlog中会记录当时主库写入的时间,拿这个时间和当前从库的时间 相减计算。
从库在主库建立连接的时候会获取主库的时间,如果和从库不一致,在算seconds_behind_master的时候会减去这部分误差。
网络正常的情况下,传输binlog是很快的,主备延迟的主要来源是备库接收完 binlog 和执行完这个事务之间的时间差。
主备延迟最直接的表现是,备库消费中转日志(relay log)的速度,比主库生产 binlog 的速度要慢。
备库延迟大的原因:
1.备库性能差
2.备库压力大
3.主库有大事务,比如主库要执行10分钟的事务,同步到从库也要执行10分钟。
比如说一次性地用 delete 语句删除太多数据,这个是典型的大事务。
大表DDL 也是典型的大事务。
主备切换:
主备切换的时候,要将备库切换为主库,需要注意这个延迟,如果备库还在落后主库的情况下被切换为主库,就会造成数据不一致,体现在业务上
就是原来的一部分最新的数据,突然查不到了。
有两种方案:
1.可靠性优先
等到备库的延迟时间seconds_behind_master 小于5秒的时候,将主库设置为只读
然后等到延迟时间为0时候,就立刻将备库切换为主库,将这个库改为可写状态。
将业务请求切换到新的主库上。
这样可以在较小的不可用时间内,将主备切换过来。
2.可用性优先
当主库已经挂了的情况下,业务又不能容许不可用
那么就要立刻将备库切换为主库。
但是可能会出现数据不一致的情况,因为有部分数据因为延迟还没被同步到备库上。 补偿方案是binlog设置为row ,这样还可以通过binlog来恢复。
一般都是强烈建议可靠性优先。
28.备库的复制
mysql数据的流程:
业务并发写入 ->记录undolog ->写redolog (prepare状态)->写binlog->redolog (commit)->返回给业务结果
有一个线程叫dump_thread 会源源不断的把binlog发送给从库。
从库的io_thread将接收的binlog都存在relaylog(中转日志)上
从库的sql_thread 将relaylog上的binlog应用到从库上。
当业务量大的时候,源源不断的binlog写到relaylog上,但是sql_thread又不够,就会导致备份延迟越来越大。
mysql5.6之前,sql_thread是单线程的,之后优化了多线程,多线程并发复制,加快复制速度。
mysql5.6 之后,sql_thread 不再直接更新数据,只负责取中转日志 和分发事务。 真正执行更新的是worker线程
worker线程的数量可以通过slave_parallel_workers参数设置。
在分发事务时候,要保证
1.不能覆盖更新,这就要求更新同一行的数据的worker,必须被分发到同一个woker中。
2.同一个事务 不能拆开。
目前常用的是按库分发。
29.主从切换
一主多从切换的时候,当切换主库(因为主库故障)的时候,选择一个从库作为主库,与此同时,其他从库监听的主库也要改为这个库。
这里面有个问题,就是同步的位置的问题。 这个位置叫位点。
修改需要6个参数
MASTER_HOST
MASTER_PORT
MASTER_USER
MASTER_PASSWORD 四个参数
分别代表了主库的 IP、端口、用户名和密码。
MASTER_LOG_FILE 和 MASTER_LOG_POS要从主库的 master_log_name 文件
的 master_log_pos 这个位置的日志继续同步。
而这个位置就是 我们所说的同步位点,也就是主库对应的文件名和日志偏移量。
这个主库的 master_log_pos 位点 就很难精确判断。
一般会取原主库出现问题的时间点,稍微往前一点。
这样就会多同步一些数据过来,这些数据如果是插入,可能造成主键或者唯一键冲突,需要忽略掉这些错误。
可以选择跳过这些错误 或者跳过这些事务。
1062 错误是插入数据时唯一键冲突; 1032 错误是删除数据时找不到行。
可以把 slave_skip_errors 设置为 “1032,1062”,这样中间碰到这两个错误时 就直接跳过。
GTID
通过 sql_slave_skip_counter 跳过事务和通过 slave_skip_errors 忽略错误的方法,虽然都最终可以建立从库 B 和新主库 A’的主备关系,但这两种操作都很复杂,而且容易出错。 所以,MySQL 5.6 版本引入了 GTID。
GTID 的全称是 Global Transaction Identifier,也就是全局事务 ID,是一个事务在提交的 时候生成的,是这个事务的唯一标识
GTID=server_uuid:gno
server_uuid 是一个实例第一次启动时自动生成的,是一个全局唯一的值;
gno 是一个整数,初始值是 1,每次提交事务的时候分配给这个事务,并加 1。
在 GTID 模式下,每个事务都会跟一个 GTID 一一对应
master_auto_position=1 就表示这个主备关系使用的是 GTID 协议
MASTER_LOG_FILE 和 MASTER_LOG_POS 参数,已经不需要指定 了。
逻辑为:
从库将自己的GTID集合发送给主库
主库计算两边GTID的差集
如果主库不包含从库的GTID,说明主库已经把从库需要的binlog删掉了,返回错误。
如果包含,就找出主库有,但是从库没有的,这部分就是从库需要同步的。
从满足以上条件的第一条往后按顺序取binlog发送给从库
这个逻辑里面包含了一个设计思想:在基于 GTID 的主备关系里,系统认为只要建立 主备关系,就必须保证主库发给备库的日志是完整的。因此,如果实例 B 需要的日志已经 不存在,A’就拒绝把日志发给 B。
这跟基于位点的主备协议不同。基于位点的协议,是由备库决定的,备库指定哪个位点,主
库就发哪个位点,不做日志的完整性判断
比如说在要给某个大表加索引的时候,为了避免加索引期间影响主库性能继而对业务产生影响,先在从库加,然后切换主从。