MySQL主从

一 主从复制介绍

1.1 什么是主从复制

复制代码
将主服务器的binlog日志复制到从服务器上执行一遍,达到主从数据的一致状态,称之为主从复制。

一句话表示就是,主数据库做什么,从数据库就跟着做什么。

1.2 为何要做主从

(1)为实现服务器负载均衡/读写分离做铺垫,提升访问速度
复制代码
#1、什么是读写分离
有了主从保持数据一致作为大前提,我们便可以可以分离读写操作,其中Master负责写操作的负载,也就是说一切写的操作都在Master上进行,而读的操作则分摊到Slave上进行。那么读写分离的作用是什么呢?
#2、读写分离的作用
先说答案:读写分离可以大大提高读取的效率。

在一般的互联网应用中,经过一些数据调查得出结论,读/写的比例大概在 10:1左右 ,也就是说写操作非常少,大量的数据操作是集中在读的操作(如某些应用,像基金净值预测的网站。其数据的更新都是有管理员更新的,即更新的用户比较少。而查询的用户数 量会非常的多。)
此时我们可以制作一主多从,因为写操作很少,所以由一个主库负责即可,而大量的读操作则分配给多个从库,这样占据比例最大的读操作的压力就被负载均衡了,因此读效率会得到了极大的提升,另外,还有一个原因就是:
熟悉DB的研发人员都知道,写操作涉及到锁的问题,不管是行锁还是表锁还是块锁,都是比较降低系统执行效率的事情。我们这样的分离是把写操作集中在一个节点上,而读操作其其他的N个节点上进行,这从另一个方面有效的提高了读的效率,保证了系统的性能及高可用性。
#3、具体做法
方案一:
就是主库写,从库读

方案二:
主库负责写,还有部分读,从库只负责读,而且是读操作的主力

即当主服务器比较忙时,部分查询请求会自动发送到从服务器中,以降低主服务器的工作负荷。
(2)通过复制实现数据的异地备份,保障数据安全
复制代码
可以定期的将数据从主服务器上复制到从服务器上,这实现了数据的异地备份。
在传统的备份体制下,是将数据备份在本地。此时备份作业与数据库服务器运行在同一台设备上,当备份作业运行时就会影响到服务器的正常运行。有时候会明显的降低服务器的性能。同时,将备份数据存放在本地,也 不是很安全。如硬盘因为电压等原因被损坏或者服务器被失窃,此时由于备份文件仍然存放在硬盘上,数据库管理员无法使用备份文件来恢复数据。这显然会给企业 带来比较大的损失。
(3)提高数据库系统的可用性
复制代码
数据库复制功能实现了主服务器与从服务器之间数据的同步,增加了数据库系统的可用性。主库宕机后,从库尚可用,即当主服务器出现问题时,数据库管理员可以马上让从服务器作为主服务器,用来数据的更新与查询服务。然后回过头来再仔细的检查主服务器的问题。此时一般数据库管理员会采用两种手段。

​一:从库临时取代主库,只用来读
主服务器故障之后,虽然从服务器取代了主服务器的位置,但是对于主服务器可以采取的操作仍然做了一些限制,例如仍然只能够进行数据的查询,而不能够进行数据的更新、删除等操作。这主要是基于从数据的安全性考虑。如现在一些银行系统的升级,在升级的过程中,只能够查询余额而不能够取钱。这是同样的道理。

​二:从库永久取代主库,负责读和写
从服务器真正变成了主服务器。当从服务器切换为主服务器之后,其地位完全与原先的主服务器相同。此时可以实现对数据的查询、更新、删除等操 作。为此就需要做好数据的安全性工作。即数据的安全策略,要与原先的主服务器完全相同。否则的话,就可能会留下一定的安全隐患

1.3 主从复制的原理

整体上来说,复制有3个步骤:
​ (1) master将改变记录到二进制日志(binary log)中(这些记录叫做二进制日志事件,binary log events)。
​ (2) slave的io线程将master的binary log events拷贝到它的中继日志(relay log);
​ (3) slave的sql线程解析中继日志中的事件并在从库执行,保持与主库一致。

复制过程有一个很重要的限制——复制在slave上是串行化的,也就是说master上的并行更新操作不能在slave上并行操作。
详解如下

复制代码

从库准备

(1)从库change master to 时,ip port user password binlog position写入到master.info进行记录
(2)从库 start slave 时,会启动IO线程和SQL线程

同步的过程

1.从库的IO线程,读取master.info信息,获取主库信息并连接主库
2.主库接收从库的链接请求后,会生成一个准备binlog DUMP的线程,来响应从库
3.主库一旦有新的日志生成,会发送“信号”给主库的binlog dump线程,然后binlog dump线程会读取binlog日志的更新
4.TP(传送)给从从库的IO线程
5.IO线程将收到的日志存储到了TCP/IP 缓存
6.写入TCP/IP缓存后,立即返回ACK给主库 ,此时主库工作完成
7.IO线程更新master.info文件binlog 文件名和postion
8.IO线程将缓存中的数据,存储到relay-log日志文件,此时io线程工作完成
9.从库SQL线程读取relay-log.info文件,获取到上次执行到的relay-log的位置,作为起点
10.从库SQL线程基于从步骤9中获取到的起点,去中继日志relay-log.000001获取后续操作,在从库回放relay-log
11.SQL线程回放完成之后,会更新relay-log.info文件,把当前操作的位置记入,作为下一次操作的起点。
12. relay-log会有自动清理的功能。

1.4 主从复制的日志格式

这里的日志格式就是指二进制日志的三种格式
binlog_format=statement
binlog_format=row
binlog_format=mixed
其中基于row的复制方式更能保证主从库数据的一致性,但日志量较大,在设置时考虑磁盘的空间问题

1.5 主从复制的方式

MySQL的主从复制有两种复制方式,分别是异步复制和半同步复制

二 异步复制

2.1 异步复制原理介绍

我们之前介绍的就是异步复制,即客户端线程提交一个写操作,写入主库的binlog日志后就立即返回,并不需要等待从库完成同步操作,而主库的dump线程会监测binlog日志的变量然后主动将更新推送给从库。
MySQL 主从复制默认是异步的模式。

2.2 异步复制部署

前提:主从数据库版本一致

2.2.1 主库停服时制作主从

一、主库:192.168.15.101
1)在主库上创建一个用于复制的账号,并赋予replication slave权限,这里必须 . 不能指定库授权,因为 replication slave 是全局的
复制代码
mysql> grant replication slave on . to 'jason'@'%' identified by '123';
mysql> flush privileges;
1.修改主库配置文件,开启主库的Binlog,并设置server-id
复制代码
[mysqld]# 节点ID,确保唯一
server-id = 1
#开启mysql的binlog日志功能
log-bin = mysql-bin#控制数据库的binlog刷到磁盘上去 , 0 不控制,性能最好,1每次事物提交都会刷到日志文件中,性能最差,最安全
sync_binlog = 1 #binlog日志格式
binlog_format = row #binlog过期清理时间
expire_logs_days = 7#binlog每个日志文件大小
max_binlog_size = 100m#binlog缓存大小
binlog_cache_size = 4m #最大binlog缓存大小
max_binlog_cache_size= 512m
#不生成日志文件的数据库,多个忽略数据库可以用逗号拼接,或者 复制黏贴下述配置项,写多行
binlog-ignore-db=mysql
#表中自增字段每次的偏移量
auto-increment-offset = 1 # 表中自增字段每次的自增量
auto-increment-increment = 1 #跳过从库错误
slave-skip-errors = all
3)重启主库
复制代码
systemctl restart mysql
4)备份主库,备份时锁表保证备份一致
复制代码
mysqldump -uroot -pEgon@123 -A -E -R --triggers --triggers --master-data=2 --single-transaction > /tmp/all.sql
5)将备份文件发送给从库
复制代码
scp /tmp/all.sql root@192.168.15.100:/tmp
二、从库:192.168.15.100
1)测试复制账号
复制代码
mysql -ujason -p123 -h 192.168.15.101
2)导入初始数据
复制代码
mysql -uroot -pEgon@123 < /tmp/all.sql
3)修改从库配置文件,增加server-id,注意这个值是唯一的,要区别于主库和其他从库
复制代码
[mysqld]
server-id = 2

relay-log = mysql-relay-bin
replicate-wild-ignore-table=mysql.%
replicate-wild-ignore-table=test.%
replicate-wild-ignore-table=information_schema.%
#从库也可以开启binlog,但通常关闭# log-bin=mysql-bin
4)重启从库
复制代码
systemctl restart mysqld
5)配置复制
先去主库查看一下binlog日志名与位置

然后在从库进行配置
复制代码
[root@slave1 ~]# mysql -uroot -pEgon@123 # 登录然后执行

change master to
master_host='192.168.15.101', -- 库服务器的IP
master_port=3306, -- 主库端口
master_user='jason', -- 主库用于复制的用户
master_password='123', -- 密码
master_log_file='mysql-bin.000001', -- 主库日志名
master_log_pos=120; -- 主库日志偏移量,即从何处开始复制
6)从库启动slave线程,并检查
复制代码
mysql> start slave;
Query OK, 0 rows affected (0.01 sec)

2.2.2 主库运行时制作主从

复制代码
1.准备纯净的从库环境
主库:192.168.15.101
从库:192.168.15.100

2.修改主库配置文件(略)
3.重启主库(略)
4.主库创建账号(略)
5.模拟主库运行,有数据写入、for i in seq 1 1000000do
mysql -uroot -p123 -e "insert db1.t1 values($i)";
sleep 1;done

6.主库全备数据#不打点备份(no)
mysqldump -uroot -p123 -A -R --triggers > /tmp/all.sql#打点备份(yes)
mysqldump -uroot -p123 -A -R --triggers --master-data=2 --single-transaction > /tmp/all.sql

7.将热备数据传达从库
scp /tmp/all.sql 192.168.15.100:/tmp

8.修改从库配置文件(略)

9.重启从库(略)

10.在从库导入全备数据(导入打点备份,原因如下)#导入不打点的数据的话,接下来主从,位置点只能去主库查看 show master status; 而数据在不停地往主库里插入,该命令查看的只是那一刻binlog的记录的位置,基于该位置同步数据会丢失所获得位置点往前一直到全备的数据,以及操作从库过程中新写入主库的数据。
#如果导入的是打点的数据,那么全量备份的起始点可以从备份文件中获得,从库可以以此为同步的基准点,去主库获取全量备份之后的数据

11.查看sql文件中的位置点(如果是打点备份的话)
该位置即主库刚刚做完全量备份时,主库binlog日志所处的位置
[root@jason ~]# head -50 /tmp/all.sql|grep 'MASTER_LOG_POS'
-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000001', MASTER_LOG_POS=129005;

12.从库配置同步
[root@slave1 ~]# mysql -uroot -pEgon@123 # 登录然后执行
change master to
master_host='192.168.15.101',
master_port=3306,
master_user='jason',
master_password='123',
master_log_file='mysql-bin.000001',
master_log_pos=129005;

13.开启SQL线程和IO线程
start slave;
14.查看主从状态
show slave status;
练习:基于上述方式制作双主同步

2.2.3 主从复制基本故障处理

复制代码
#清空主从配置
reset slave all;
(1)IO线程问题
复制代码
1.检测网络:
[root@db01 ~]# ping 172.16.1.51

2.检测端口:
[root@db01 ~]# telnet 172.16.1.51 3307
#阿里云机器有的禁ping
[root@db01 ~]# tcping 172.16.1.51 3306

3.检测账号,密码:
#根据授权命令
grant replication slave on . to rep@'172.16.1.5%' identified by '123';
#连接测试
mysql -urep -p123 -h127.0.0.1 -P 3307

4.查看binlog是否存在
show master status;

5.反向解析
#没有跳过反向解析
ERROR 1045 (28000): Access denied for user 'root'@'db01' (using password: YES)
#配置
skip_name_resolve
(2)SQL线程问题
复制代码
1.主库有的数据,从库没有
主库:a库
从库:没有a库
2.从库有的数据,主库没有
从库:a库
主库:要创建a库
3.主库与从库数据库结构有差别
#处理方法一:
#临时停止同步
mysql> stop slave;
#将同步指针向下移动一个(可重复操作)解决不了根本问题
mysql> set sql_slave_skip_counter=1;
#开启同步
mysql> start slave;
#处理方法二:
#编辑配置文件
[root@db01 ~]# vim /etc/my.cnf
#在[mysqld]标签下添加以下参数
slave-skip-errors=1032,1062,1007
1007:对象已存在
1032:无法执行DML
1062:主键冲突,或约束冲突
#处理方法三:
1.要求,主从复制之前,主库和从库的数据保证一致.
2.在从库上设置 只读:set read-only=1;
(做读写分离时使用,但是做MHA会出现提升为主库时,主库只读,后面会讲专门做读写分离的Atlas)

2.2.4 延迟从库

复制代码
我们说用延时从库可以做备份,主库执行删除的时候,从库还没有删除,可以把表数据拿出来恢复回去
企业中一般会延时3-6小时
(1)延时从库配置
1)已经有主从的情况下
复制代码
1.停止主从
mysql> stop slave;
2.设置延时为180秒
mysql> CHANGE MASTER TO MASTER_DELAY = 180;
3.开启主从
mysql> start slave;
4.查看状态
mysql> show slave status \G
SQL_Delay: 180
5.主库创建数据,会看到从库值变化,创建的库没有创建
SQL_Remaining_Delay: 170
2)没有主从复制的情况下
复制代码
1.修改主库,从库配置文件
server_id
开启binlog

2.保证从库和主库的数据一致

3.执行change语句
change master to
master_host='172.16.1.50',
master_user='rep',
master_password='123',
master_log_file='mysql-bin.000001',
master_log_pos=2752,
master_delay=180;
3)延时从库停止方法
复制代码
1.停止主从
mysql> stop slave;

2.设置延时为0
mysql> CHANGE MASTER TO MASTER_DELAY = 0;

3.开启主从
mysql> start slave;
#注:做延时从库只是为了备份,不提供服务
(2)思考:延时到底是在哪里延时的
复制代码
思考:IO线程还是SQL线程做的手脚?
#去主库创建一个库
create database ttt;#查看从库的relaylog看看有没有内容
mysqlbinlog --base64-output=decode-rows -vvv db03-relay-bin.000002

总结:
延时从库是在SQL线程做的手脚,IO线程已经把数据放到relay-log里了.
SQL线程在执行的时候,会延迟你设定的时间长度.
(3)使用延时从库恢复数据
1.场景
复制代码
总数据量级500G,正常备份去恢复需要1.5-2小时
1)配置延时3600秒
mysql> CHANGE MASTER TO MASTER_DELAY = 3600;

2)主库
drop database db;

3)怎么利用延时从库,恢复数据?
2.环境准备
复制代码
1.进行每日的全备
mysqldump -uroot -p123 -A -R --triggers --master-data=2 –single-transaction > /backup/full.sql

2.调整延时从库延时时间为60分钟
stop slave;
CHANGE MASTER TO MASTER_DELAY = 3600;
start slave;

3.主库写入新数据
create database yanshi;
use yanshi;
create table yanshi(id int);
insert into yanshi values(1),(2),(3),(4);
create database yanshi2;
use yanshi2;
create table yanshi2(id int);
insert into yanshi2 values(1),(2),(3),(4);
3.模拟删除数据
复制代码
1.删除一个库,可以是之前的,也可以是刚创建的
#因为刚我们看了,只要我执行了,从库已经写到了relay-log,跟我数据库里的数据关系不大
drop database world;
4.使用延时从库恢复数据
复制代码
1.停止从库sql线程
stop slave sql_thread;
2.查看状态
show slave status;
3.备份从库数据
mysqldump -uroot -p123 -B world > /backup/congku.sql
4.截取一下relay-log
1)确认起点,查看relay-log.info即可
[root@db02 data]# cat relay-log.info
./db02-relay-bin.000005
283
2)确认终点,找到drop语句之前
[root@db02 data]# mysqlbinlog --base64-output=decode-rows -vvv db02-relay-bin.000005
3)截取数据
[root@db02 data]# mysqlbinlog --start-position=283 --stop-position=1112 db02-relay-bin.000005 > /tmp/yanshi.sql
5.将从库全备的数据与relaylog截取数据拷贝到主库
scp /tmp/yanshi.sql 172.16.1.51:/tmp/
scp /backup/congku.sql 172.16.1.51:/tmp/
6.将数据导入主库
#导入前不要停掉binlog,
mysql < /tmp/yanshi.sql
mysql < /tmp/congku.sql
7.开启从库的sql线程
start slave sql_thread;
#主库那边执行到删除的时候没关系,因为还有创建的部分,他会再次把数据创建回来

2.3 异步复制的问题

MYSQL5.5之前版本的主从复制都是异步(asynchronous)的,而在异步复制中,主库执行完操作后,写入binlog日志后,就返回客户端,这一动作就结束了,主库并不会验证从库有没有收到binlog日志、以及收到的binlog是否完整,那如果主库提交一个事务并写入Binlog中后,当从库还没有从主库得到Binlog时,主库宕机了或因磁盘损坏等故障导致该事务的Binlog丢失了,那从库就不会得到这个事务,也就造成了从库与主库数据不一致的问题,我们也就无法使用备库来继续提供数据一致的服务了,半同步复制可以解决该问题。

三 半同步复制

3.1 半同步复制原理介绍

从MYSQL5.5开始,支持半同步复制(Semi synchronous Replication)在一定程度上保证提交的事务已经传给了至少一个备库。
复制代码
1、一个事务操作的完成需要记完两份日志,即主从的binlog是同步的
半同步复制,当主库每提交一个事务后,不会立即返回,而是等待其中一个从库接收到Binlog并成功写入Relay-log中才返回客户端,所以这样就保证了一个事务至少有两份日志,一份保存在主库的Binlog,另一份保存在其中一个从库的Relay-log中,从而保证了数据的安全性和一致性。

2、半同步即并非完全同步
半同步复制的“半”体现在,虽然主从库的Binlog是同步的,但主库不会等待从库的sql线程执行完Relay-log后才返回,而是确认从库的io线程接收到Binlog,达到主从Binlog同步的目的后就返回了,所以从库的数据对于主库来说还是有延时的,这个延时就是从库sql线程执行Relay-log的时间。所以只能称为半同步。

3、半同步复制超时则会切换回异步复制,正常后则切回半同步复制
在半同步复制时,如果主库的一个事务提交成功了,在推送到从库的过程当中,从库宕机了或网络故障,导致从库并没有接收到这个事务的Binlog,此时主库会等待一段时间(这个时间由rpl_semi_sync_master_timeout的毫秒数决定),如果这个时间过后还无法推送到从库,那MySQL会自动从半同步复制切换为异步复制,当从库恢复正常连接到主库后,主库又会自动切换回半同步复制。

3.2 半同步复制开启方法

半同步模式是作为MySQL5.5的一个插件来实现的,主从库使用的插件不一样
(1)先确认主从的MySQL服务器是否支持动态增加插件
复制代码
mysql> select @@have_dynamic_loading;
+------------------------+
| @@have_dynamic_loading |
+------------------------+
| YES |
+------------------------+
1 row in set (0.00 sec)
(2)分别在主从库上安装对用插件
复制代码
#插件一般默认在MySQL安装目录/lib/plugin下,可以去查看一下是否存在ls /usr/lib64/mysql/plugin/ | grep semisync

#主库的插件是semisync_master.so,从库是semisync_slave.so

主库> install plugin rpl_semi_sync_master soname 'semisync_master.so';

从库> install plugin rpl_semi_sync_slave soname 'semisync_slave.so';
#安装完成后,在plugin表(系统表)中查看一下
select * from mysql.plugin;
(3)在主从库开启半同步复制
主库
复制代码
#启动插件
mysql> set global rpl_semi_sync_master_enabled=1;#设置超时
mysql> set global rpl_semi_sync_master_timeout=30000;
#修改配置文件
[root@db01 ~]# vim /etc/my.cnf#在[mysqld]标签下添加如下内容(不用重启库,因为上面已经开启了)
[mysqld]
rpl_semi_sync_master_enabled=1
rpl_semi_sync_master_timeout=1000
从库
复制代码
#启动插件
mysql> set global rpl_semi_sync_slave_enabled=1;
#重启io线程使其生效
mysql> stop slave io_thread;
mysql> start slave io_thread;
#编辑配置文件(不需要重启数据库)
[root@mysql-db02 ~]# vim /etc/my.cnf
#在[mysqld]标签下添加如下内容
[mysqld]
rpl_semi_sync_slave_enabled =1
(4)在主库上查看半同步复制的状态
复制代码
mysql>show status like '%semi_sync';

在输出信息中,我们重点关注三个参数:
#ON表示半同步复制打开,OFF表示关闭
rpl_semi_sync_master_status OFF/ON
#这个数字表示主库当前有几个事务说通过半同步复制到从库的
rpl_semi_sync_master_yes_tx [number]
#表示有几个事务不是通过半同步复制到从库的
rpl_semi_sync_master_no_tx [number]
四 过滤复制

4.1 企业诸多环境介绍

实际情况中,企业中的环境可能有很多,如下所示
(1) 开发环境
复制代码
开发开发完自己测试
(2) 测试环境
复制代码
1)性能测试
2)功能测试
(3) 预发布环境(beta,内测不删档)
复制代码
(1)、只是一台服务器

(2)、没有真实的流量

(3)、连的是线上数据库

疑问:如果有一个待上线需求,需要改动数据库表结构,怎么处理?

先把预发布环境使用的数据库切换为测试环境使用的数据库
然后有针对性的测试下数据库的变更是否会影响线上当前代码程序的运行,测试通过后再上线
(4)灰度环境
复制代码
(1)、1台或多台线上主机

(2)、链接的是线上数据库

(3)、真实流量

灰度发布,又称金丝雀发布。
金丝雀发布这一术语源于煤矿工人把笼养的金丝雀带入矿井的传统。矿工通过金丝雀来了解矿井中一氧化碳的浓度,如果一氧化碳的浓度过高,金丝雀就会中毒,从而使矿工知道应该立刻撤离。

灰度发布发生在预发布环境之后,生产环境之前。
对应到软件开中,则是指在发布新的产品特性时通过少量的用户试点确认新特性没有问题,确保无误后推广到更大的用户使用群体。

生产环境一般会部署在多台机器上,以防某台机器出现故障,这样其他机器可以继续运行,不影响用户使用。灰度发布会发布到其中的几台机器上,验证新功能是否正常。如果失败,只需回滚这几台机器即可。
(5)沙盒环境
复制代码
沙盒环境又称测试环境和开发环境,是提供给开发者开发和测试用的环境。
沙盒通常严格控制其中的程序所能访问的资源,比如,沙盒可以提供用后即回收的磁盘及内存空间。在沙盒中,网络访问、对真实系统的访问、对输入设备的读取通常被禁止或是严格限制。

也就是说所谓的沙盒测试就是在产品未上线前在内部环境或网络下进行的测试,此时在正常的线上环境是无法看到或查询到该产品或项目的,只有产品在测试环境下无问题上传到生产环境之后,用户才能看到该产品或功能
(6) 生产环境
复制代码
除了生产和预发布其他的环境都是虚拟机测试用的

测试环境有很多游戏,我就想一个从库同步一种游戏,还有合服,建新服,其实就是一个库或者一个表而已.
考虑到环境过多为了节省资源,我们会有只同步某一个库的需求。这就用到了黑名单与白名单

4.2 过滤复制的两种方式

(1)黑名单
复制代码
不记录黑名单列出的库的二进制日志
#参数
replicate-ignore-db=test
replicate-ignore-table=test.t1
replicate-wild-ignore-table=test.t% 支持通配符,t开头的表

注意:
replicate-ignore-table依赖参数replicate-ignore-db
即如果想忽略某个库下的某张表,需要一起配置
replicate-ignore-db=test
replicate-ignore-table=test.t1
(2)白名单
复制代码
只执行白名单中列出的库或者表的中继日志
#参数:
replicate-do-db=test
replicate-do-table=test.t1
replicate-wild-do-table=test.t%

注意:
replicate-do-table依赖参数replicate-do-db
即如果想只接收某个库下的某张表,需要一起配置
replicate-do-db=test
replicate-do-table=test.t1
(3)主从库设置黑白名单的影响
复制代码
#1、黑白名单对主库的影响是:是否记录binlog日志
在主库上设置白名单:只记录白名单设置的库或者表、相关的SQL语句到binlog中
在主库上设置黑名单:不记录黑名单设置的库或者表、相关的SQL语句到binlog中
#2、黑白名单对从库的影响是:sql线程是否执行io线程拿到的binlog
IO线程一定会拿到所有的binlog,但

如果在从库上设置白名单:SQL线程只执行白名单设置的库或者表相关的SQL语句

如果在从库上设置黑名单:SQL线程不执行黑名单设置的库或者表相关的SQL语句

4.3 配置过滤复制

(1)做好一主两从
复制代码
#如果查看从库show slave status\G报错
A slave with the same server_uuid/server_id as this slave has connected to the master;# 解决方案如下
如果server_id相同,请修改/etc/my.cnf中server_id的配置

如果server_uuid相同,请删除auto.cnf文件(auto.cnf文件在/etc/my.cnf中datadir配置的目录下),然后重启数据库,数据库会重新生成server_uuid和auto.cnf文件
[rml_read_more]:
(2)从库1配置
复制代码
[root@db02 ~]# vim /etc/my.cnf
[mysqld]
replicate-do-db=big_jason

[root@db02 ~]# systemctl restart mysql
#查看主从状态
show slave status;
(3)从库2配置
复制代码
[root@db03 ~]# vim /etc/my.cnf
[mysqld]
replicate-wild-do-table=big_jason.t%
replicate-do-db=small_jason
replicate-do-table=small_jason.t1

[root@db03 ~]# systemctl restart mysql
#查看主从状态
show slave status;
(4)主库创建表测试
复制代码
create database big_jason;
create database small_jason;

use big_jason;
create table t1(id int); -- 表名以t开头
create table t2(id int); -- 表名以t开头
create table x3(id int);

use small_jason;
create table t1(id int);
create table t2(id int);
create table t3(id int);
5)从库查看表
复制代码
从库1上只能看到big_jason库下的所有表:t1、t2、x3

从库2上只能看到
big_jason库下t开头的那两张表,看不到表x3
small_jason库下的t1表
6)自行练习验证:尝试把白名单配置到主库
复制代码
[root@db01 ~]# vim /etc/my.cnf
[mysqld]
replicate-do-db=db1

[root@db01 ~]# systemctl restart mysql
#测试同步

五 主从复制的架构

5.1 主备架构,只有主库提供读写服务,备库仅留作备用

复制代码
jdbc:mysql://vip:3306/xxdb
1、高可用分析:高可用,主库挂了,keepalive(只是一种工具)会自动切换到备库。这个过程对业务层是透明的,无需修改代码或配置。
2、高性能分析:读写都操作主库,很容易产生瓶颈。大部分互联网应用读多写少,读会先成为瓶颈,进而影响写性能。另外,备库只是单纯的备份,资源利用率50%,这点方案二可解决。
3、一致性分析:读写都操作主库,不存在数据一致性问题。
4、扩展性分析:无法通过加从库来扩展读性能,进而提高整体性能。
5、可落地分析:两点影响落地使用。第一,性能一般,这点可以通过建立高效的索引和引入缓存来增加读性能,进而提高性能。这也是通用的方案。第二,扩展性差,这点可以通过分库分表来扩展。

5.2 双主架构,两个主库同时提供服务,负载均衡

复制代码
jdbc:mysql://vip:3306/xxdb
1、高可用分析:高可用,一个主库挂了,不影响另一台主库提供服务。这个过程对业务层是透明的,无需修改代码或配置。
2、高性能分析:读写性能相比于方案一都得到提升,提升一倍。
3、一致性分析:存在数据一致性问题。请看,一致性解决方案。
4、扩展性分析:当然可以扩展成三主循环,但笔者不建议(会多一层数据同步,这样同步的时间会更长)。如果非得在数据库架构层面扩展的话,扩展为方案四。
5、可落地分析:两点影响落地使用。第一,数据一致性问题,一致性解决方案可解决问题。第二,主键冲突问题,ID统一地由分布式ID生成服务来生成可解决问题。

5.3 主从架构,一主多从,读写分离

复制代码

jdbc:mysql://master-ip:3306/xxdb


jdbc:mysql://slave1-ip:3306/xxdb
jdbc:mysql://slave2-ip:3306/xxdb
1、高可用分析:主库单点,从库高可用。一旦主库挂了,写服务也就无法提供。
2、高性能分析:大部分互联网应用读多写少,读会先成为瓶颈,进而影响整体性能。读的性能提高了,整体性能也提高了。另外,主库可以不用索引,线上从库和线下从库也可以建立不同的索引(线上从库如果有多个还是要建立相同的索引,不然得不偿失;线下从库是平时开发人员排查线上问题时查的库,可以建更多的索引)。
3、一致性分析:存在数据一致性问题。请看,一致性解决方案。
4、扩展性分析:可以通过加从库来扩展读性能,进而提高整体性能。(带来的问题是,从库越多需要从主库拉取binlog日志的端就越多,进而影响主库的性能,并且数据同步完成的时间也会更长)
5、可落地分析:两点影响落地使用。第一,数据一致性问题,一致性解决方案可解决问题。第二,主库单点问题,笔者暂时没想到很好的解决方案。
注:思考一个问题,一台从库挂了会怎样?读写分离之读的负载均衡策略怎么容错?

5.4 级联复制架构(Master –Slaves - Slaves)

因为每个从库在主库上都会有一个独立的Binlog Dump线程来推送binlog日志,所以随着从库数量的增加,主库的IO压力和网络压力也会随之增加,这时,多级复制架构应运而生。
多级复制架构只是在一主多从的基础上,再主库和各个从库之间增加了一层二级主库Master2,这层二级主库仅仅用来将一级主库推送给它的BInlog日志再推送给各个从库,以此来减轻一级主库的推送压力。
但它的缺点就是Binlog日志要经过两次复制才能到达从库,增加了复制的延时。
我们可以通过在二级从库上应用Blackhol存储引擎(黑洞引擎)来解决这一问题,降低多级复制的延时。
“黑洞引擎”就是写入Blackhole表中数据并不会写到磁盘上,所以这个Blackhole表永远是个空表,对数据的插入/更新/删除操作仅在Binlog中记录,并复制到从库中去。

5.5 双主+主从架构,看似完美的方案

复制代码

jdbc:mysql://vip:3306/xxdb


jdbc:mysql://slave1-ip:3306/xxdb
jdbc:mysql://slave2-ip:3306/xxdb
1、高可用分析:高可用。
2、高性能分析:高性能。
3、一致性分析:存在数据一致性问题。请看,一致性解决方案 。
4、扩展性分析:可以通过加从库来扩展读性能,进而提高整体性能。(带来的问题同方案二)
5、可落地分析:同方案二,但数据同步又多了一层,数据延迟更严重。
六 一致性解决方案

6.1 导致主从延时因素

注:图中圈出的是数据同步的地方
数据同步(从主库把binlog日志推送给从库,然后在从库再执行一遍)是需要时间的,这个同步时间内主库和从库的数据会存在不一致的情况,遇到网络延迟,或者主库并发事务过多的情况(主库可以并行事务,但从库的是SQL线程串行回放的,如果并发事务过大,从库必然忙活不过来,造成延迟),都会加剧不一致的情况。
如果同步过程中有读请求,那么读到的就是从库中的老数据。如下图。

查看主从延迟
复制代码
#延时指标1:查看从库获取到主库binlog的时间
show slave status;
查看延时时间
Seconds_Behind_Master: 0

#延时指标2:查看从库回放的日志量与主库日量的差
主库:show master status ;
从库: show slave status \G

查看从库已经拿到的主库日志量
Master_Log_File: mysql-bin.000003
Read_Master_Log_Pos: 172826

查看从库已经执行的主库日志量
Relay_Master_Log_File: mysql-bin.000003
Exec_Master_Log_Pos: 131837
既然知道了数据不一致性产生的原因,那如何解决呢?
首先如果业务允许延时存在,那么就不去管它,如果对一致性确实有要求,那么可以围绕以下角度去优化

6.2 硬件方面

复制代码
1.采用好服务器,比如4u比2u性能明显好,2u比1u性能明显好。

2.存储用ssd或者盘阵或者san,提升随机写的性能。

3.主从间保证处在同一个交换机下面,并且是万兆环境。

总结,硬件强劲,延迟自然会变小。一句话,缩小延迟的解决方案就是花钱和花时间。

6.3 文件系统方面

master端修改linux、Unix文件系统中文件的etime属性, 由于每当读文件时OS都会将读取操作发生的时间回写到磁盘上,对于读操作频繁的数据库文件来说这是没必要的,只会增加磁盘系统的负担影响I/O性能。可以通过设置文件系统的mount属性,组织操作系统写atime信息,在linux上的操作为:打开/etc/fstab,加上noatime参数/dev/sdb1 /data reiserfs noatime 1 2然后重新mount文件系统#mount -o remount /data

6.4 优化mysql参数

复制代码
1、logs-slave-updates 从服务器从主服务器接收到的更新不记入它的二进制日志。

2、sync_binlog在slave端设置为0或禁用slave端的binlog

3、slave端,如果使用的存储引擎是innodb,innodb_flush_log_at_trx_commit =2

对数据安全性较高,需要设置
sync_binlog=1
innodb_flush_log_at_trx_commit = 1
而对于slave则不需要这么高的数据安全,完全可以设置为
sync_binlog=0或者关闭binlog
innodb_flush_log_at_trx_commit =0或2

6.5 主从都开启GTID复制模式

复制代码
从MySQL 5.6.5 开始新增了一种基于 GTID 的复制方式。通过 GTID 保证了每个在主库上提交的事务在集群中有一个唯一的ID。这种方式强化了数据库的主备一致性,故障恢复以及容错能力。

GTID (Global Transaction ID)是全局事务ID,当在主库上提交事务或者被从库应用时,可以定位和追踪每一个事务,对DBA来说意义就很大了,我们可以适当的解放出来,不用手工去可以找偏移量的值了,而是通过CHANGE MASTER TO MASTER_HOST='xxx', MASTER_AUTO_POSITION=1的即可方便的搭建从库,在故障修复中也可以采用MASTER_AUTO_POSITION=‘X’的方式。

可能大多数人第一次听到GTID的时候会感觉有些突兀,但是从架构设计的角度,GTID是一种很好的分布式ID实践方式,通常来说,分布式ID有两个基本要求:
1)全局唯一性
2)趋势递增

这个ID因为是全局唯一,所以在分布式环境中很容易识别,因为趋势递增,所以ID是具有相应的趋势规律,在必要的时候方便进行顺序提取,行业内适用较多的是基于Twitter的ID生成算法snowflake,所以换一个角度来理解GTID,其实是一种优雅的分布式设计。

5.7版本GTID做了增强,不手工开启也自动维护匿名的GTID信息

5.7 版本的从库并发配置方法
gtid_mode=ON #开启GTID复制模式
enforce_gtid_consistency=ON #强制GTID一致性
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=16
master_info_repository=TABLE #将master-info信息记录到表中
relay_log_info_repository=TABLE #将relay-info信息记录到表中
relay_log_recovery=ON

6.6 架构方面

首先,如果业务允许延时存在,那么就不去管数据一致问题。如果需要考虑延时,架构方案有如下几种
(1)、强制读主
采用主备架构方案,读写都走主库。此时肯定不会出现数据不一致问题,因为读写都打到了一个库上,刚刚写完就可以立即读到。
但从库一直被闲置,主库的压力太大了,为了缓解主库的读压力,可以引入缓存。
但是有一点需要知道:如果缓存挂了,可能会产生雪崩现象,不过一般分布式缓存都是高可用的。
在本架构中,从库的作用是,主库一旦挂掉,vip则漂移到从库上,从库可以投入使用。

具体步骤分析如下

1:写操作
ow1.delete key先把写入的数据更新进缓存,保障可以立即读到
o然后w2.write,把数据写入主库
2、读操作
or1.get key先从缓存里读数据,如果读到则返回给用户
o如果没有读到,则r2.read读取主库
o然后r3.set key将读到的数据更新进缓存,方便下一次读
(2)选择读主
写操作时根据库+表+业务特征生成一个key放到Cache里
并且为key设置超时时间,而该key的超时时间>=主从数据同步的时间,这保证了一件事,就是当缓存数据超时时,从库中一定同步好了数据,此时我们可以读从库。

1:写操作
ow1.delete key先把写入的数据更新进缓存,保障可以立即读到,并且设置缓存的超时时间,该时间>=主从同步的延迟时间
o然后w2.write,把数据写入主库
2、读操作
or1.get key先从缓存里读数据,如果读到则返回给用户
o如果没有读到,则证明该数据在缓存中超时了,因为该超时时间是>=主从同步的时间的,所以此时从库也一定是有数据了,我们直接r2.read去从库读即可
o然后r3.set key将读到的数据更新进缓存,方便下一次读
总结一下缓存使用方式:
​ 第一步:淘汰缓存-》delete key;
​ 第二步:写入数据库;
​ 第三步:读取缓存,命中则返回,否则去数据库中读;
​ 第四步:读取数据库后写入缓存。
4、半同步复制,等主从同步完成,写请求才返回。就是大家常说的“半同步复制”semi-sync。这可以利用数据库原生功能,实现比较简单。代价是写请求时延增长,吞吐量降低。
5、数据库中间件,引入开源(mycat等)或自研的数据库中间层。个人理解,思路同2。数据库中间件的成本比较高,并且还多引入了一层。

6.7 架构演变

1、架构演变一:方案一 -> 方案一+分库分表 -> 方案二+分库分表 -> 方案四+分库分表;
2、架构演变二:方案一 -> 方案一+分库分表 -> 方案三+分库分表 -> 方案四+分库分表;
3、架构演变三:方案一 -> 方案二 -> 方案四 -> 方案四+分库分表;
4、架构演变四:方案一 -> 方案三 -> 方案四 -> 方案四+分库分表;

总结

1、加缓存和索引是通用的提升数据库性能的方式;
2、分库分表带来的好处是巨大的,但同样也会带来一些问题,详见前日推文。
3、不管是主备+分库分表还是主从+读写分离+分库分表,都要考虑具体的业务场景。绝大部分的数据库架构还是采用方案一和方案一+分库分表,只有极少部分用方案三+读写分离+分库分表。另外,阿里云提供的数据库云服务也都是主备方案,要想主从+读写分离需要二次架构。
4、记住一句话:不考虑业务场景的架构都是耍流氓

Q.E.D.