概述
- 一般应用对数据库而言都是“ 读多写少 ”,也就说对数据库读取数据的压力比较大,有一个思路就是采用数据库集群的方案,做
主从架构、进行读写分离,这样同样可以提升数据库的并发处理能力。但并不是所有的应用都需要对数据库进行主从架构的设置,毕竟设置架构本身是有成本的。 - 如果我们的目的在于提升数据库高并发访问的效率,那么首先考虑的是如何 优化SQL和索引 ,这种方式简单有效;其次才是采用 缓存的策略 ,比如使用 Redis将热点数据保存在内存数据库中,提升读取的效率;最后才是对数据库采用 主从架构 ,进行读写分离。
主从复制的作用
- 可以实现读写分离
- 主库Master负责数据的写入,从库Slave负责数据的读取
- 主库更新的时候,会通过主从复制将变更同步到从库中。
- 对于“读多写少”,读写分离可以实现更高的并发访问。通过负载均衡,将请求分发到多个服务器上。同时由于读写分离,减少了锁表的影响。
- 进行数据备份:通过主从复制将主库上的数据复制到从库上,相当于是一种热备份机制。
- 实现数据库的高可用性:当主库出现故障,可以切换到从库上,保障服务的正常运行。
主从复制原理
- Slave 会从 Master 读取 binlog 来进行数据同步。
原理剖析
涉及到的3个线程,1个主库线程,2个从库线程
- 二进制日志转储线程 (Binlog dump thread)是一个主库线程。当从库线程连接的时候, 主库可以将二进制日志发送给从库,当主库读取事件(Event)的时候,会在 Binlog 上 加锁 ,读取完成之后,再将锁释放掉。
- 从库 I/O 线程 会连接到主库,向主库发送请求更新 Binlog。这时从库的 I/O 线程就可以读取到主库的二进制日志转储线程发送的 Binlog 更新部分,并且拷贝到本地的中继日志 (Relay log)。
- 从库 SQL 线程 会读取从库中的中继日志,并且执行日志中的事件,将从库中的数据与主库保持同步。
过程如下,其中也包含上述3个线程

注意:主从复制需要保证binlog开启。默认情况下从服务器会执行所有主服务器中保存的事件,也可以通过配置,只执行特定的事件。
三个步骤
- 步骤1: Master 将写操作记录到二进制日志( binlog )。
- 步骤2: Slave 将 Master 的binary log events拷贝到它的中继日志( relay log );
- 步骤3: Slave 重做中继日志中的事件,将改变应用到自己的数据库中。 MySQL复制是异步的且串行化的,而且重启后从 接入点 开始复制。
主从复制最大的问题:延时
主从复制基本原则
- 每个 Slave 只有一个 Master
- 每个 Slave 只能有一个唯一的服务器ID
- 每个 Master 可以有多个 Slave
一主一从架构搭建
- 主从都在
/etc/my.cnf配置文件中进行配置,不要使用临时配置 - 主从配置项全都在配置文件的
[mysqld]节点下,全都使用小写字母
主机配置
- 必选
#[必须]主服务器唯一IDserver-id=1#[必须]启用二进制日志,指明路径。比如:自己本地的路径/log/mysqlbinlog-bin=/usr/local/etc/mysqlbinlog/binlog
- 可选
#[可选] 0(默认)表示读写(主机),1表示只读(从机) read-only=0 #设置日志文件保留的时长,单位是秒 binlog_expire_logs_seconds=6000 #控制单个二进制日志大小。此参数的最大和默认值是1GB max_binlog_size=200M #[可选]设置不要复制的数据库 binlog-ignore-db=test #[可选]设置需要复制的数据库,默认全部记录。比如:binlog-do-db=mytest_master_slave binlog-do-db=需要复制的主数据库名字 #[可选]设置binlog格式 binlog_format=STATEMENT
- 主库配置

修改配置文件后需要重启mysql
从机配置
- 必选
#[必须]从服务器唯一ID server-id=2
- 可选
#[可选]启用中继日志 relay-log=mysql-relay
- 从机配置

重启mysql服务
主机建立账户并授权
- 创建账户并授权Slave
```
创建账户
CREATE USER ‘slave1’@’%’ IDENTIFIED BY ‘123456’;授权主从复制
GRANT REPLICATION SLAVE ON . TO ‘slave1’@’%’;需要执行 否则报下图的错
ALTER USER ‘slave1’@’%’ IDENTIFIED WITH mysql_native_password BY ‘123456’;
flush privileges;
- 到这里,主机的配置就结束了,查看主机的状态信息
> `show master status ;`
>
> 
<a name="c6d9266b"></a>
### 从机:配置需要复制的主机
- 配置从机复制的主机信息
CHANGE MASTER TO MASTER_HOST=’主机的IP地址’, MASTER_USER=’主机用户名’, MASTER_PASSWORD=’主机用户名的密码’, MASTER_LOG_FILE=’主机binlog’, MASTER_LOG_POS=具体值; ```
- 启动Slave进行同步:
START SLAVE如果如下报错

- 查看同步状态

两个都是yes表示成功
测试
- 首先,在主机中创建需要同步的数据库


- 主机创建表

- 主机添加记录

停止主从同步停止主从同步命令(从机):stop slave;
- 停止主从同步命令(从机):
stop slave;
主机配置项binlog_format
- 用于设置binlog格式
binlog_format=STATEMENT:基于SQL语句的复制(statement-based replication, SBR),默认格式- binlog中记录的是每一条修改的SQL语句
- 优点
- 历史悠久,技术成熟
- 不需要记录每一行的变化,减少了binlog日志量,文件较小
- binlog中包含了所有数据库更改信息,可以据此来审核数据库的安全等情况,可以通过SQL语句查看究竟做了什么操作
- binlog可以用于实时的还原,而不仅仅用于复制
- 主从版本可以不一样,从服务器版本可以比主服务器版本高
- 缺点
- 不是所有的UPDATE语句都能被复制,尤其是包含不确定操作的时候,比如
date(),LOAD_FILE()、UUID()、USER()、FOUND_ROWS()、SYSDATE()
(除非启动时启用了 —sysdate-is-now 选项)
- 不是所有的UPDATE语句都能被复制,尤其是包含不确定操作的时候,比如
binlog_format=ROW:基于行的复制(row-based replication, RBR)- MySQL5.5开始支持,仅记录哪条数据被修改了,修改成什么样了。
- 优点:
- 任何情况都可以被复制,这对复制来说是最安全可靠的。(比如:不会出现某些特定情况下的存储过程、function、trigger的调用和触发无法被正确复制的问题)
- 相对于SBR来说,复制以下几种语句时的行锁更少:INSERT … SELECT、包含 AUTO_INCREMENT 字段的 INSERT、没有附带条件或者并没有修改很多记录的 UPDATE 或 DELETE 语句
- 执行 INSERT,UPDATE,DELETE 语句时锁更少
- 从服务器上采用 多线程 来执行复制成为可能
- 缺点:
- binlog 大了很多
- 复杂的回滚时 binlog 中会包含大量的数据
- 主服务器上执行 UPDATE 语句时,所有发生变化的记录都会写到 binlog 中,而 SBR 只会写一次,这会导致频繁发生 binlog 的并发写问题
- 无法从 binlog 中看到都复制了些什么语句
binlog_format=MIXED:混合模式复制(mixed-based replication, MBR)- 从5.1.8版本开始,MySQL提供了Mixed格式,实际上就是Statement与Row的结合。
- 在Mixed模式下,一般的语句修改使用statment格式保存binlog。如一些函数,statement无法完成主从复制的操作,则采用row格式保存binlog。
- MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在Statement和Row之间选择一种。
其他
- 以上我们搭建了一主一从架构,但是并不具备读写分离的功能。
- 通常,可以使用数据库中间件来实现读写分离,比如MyCat

主从同步数据一致性问题
- 主从数据不一致的原因主要是主从延迟导致的,主要有两方面:
- 主从同步是通过binlog二进制文件进行的,在网络传输过程中存在着延迟。
- 从库接收完binlog后到执行完事务之间的时间差。
- 如何减少主从延迟?
- 降低多线程大事务并发的概率,优化业务逻辑
- 优化SQL,避免慢SQL, 减少批量操作 ,建议写脚本以update-sleep这样的形式完成。
- 提高从库机器的配置 ,减少主库写binlog和从库读binlog的效率差。
- 尽量采用 短的链路 ,也就是主库和从库服务器的距离尽量要短,提升端口带宽,减少binlog传输的网络延时。
- 实时性要求的业务读强制走主库,从库只做灾备,备份。
解决一致性问题
- 这里的一致性是最终一致性
- 读写分离情况下,解决主从同步中数据不一致的问题, 就是解决主从之间数据复制方式的问题,如果按照数据一致性从弱到强 来进行划分,有以下 3 种复制方式。
异步复制
- 默认场景,数据一致性最弱
- 主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理。这样就会有一个问题,主库如果crash掉了,此时主库上已经提交的事务可能并没有传到从上,如果此时强行将从提升为主,可能导致新主上的数据不完整。

半同步复制
- MySQL5.5之后开始支持
- 主库在执行完客户端提交的事务之后不立即将结果返回给客户端。而是至少有一个(可配置)从库接收到Binlog,并写入中继日志中,再返回给客户端。
- 相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。
- MySQL5.7中增加了参数
rpl_semi_sync_master_wait_for_slave_count设置应答的从库的个数
组复制
- 异步复制和半同步复制都无法保证数据的一致性问题(保证最终一致性)
- 而对实时性要求更高的场景,可以采用组复制技术((MySQL Group Replication,MGR),采用Paxos协议的状态机复制。
- 首先我们将多个节点共同组成一个复制组,在 执行读写(RW)事务 的时候,需要通过一致性协议层(Consensus 层)的同意,也就是读写事务想要进行提交,必须要经过组里“大多数人”(对应 Node 节点)的同意,大多数指的是同意的节点数量需要大于 (N/2+1),这样才可以进行提交,而不是原发起方一个说了算。
- 而针对 只读(RO)事务 则不需要经过组内同意,直接 COMMIT 即可。
- 在一个复制组内有多个节点组成,它们各自维护了自己的数据副本,并且在一致性协议层实现了原子消息和全局有序消息,从而保证组内数据的一致性。

