MySQL主从模式是指数据可以从一个MySQL数据库服务器主节点复制到一个或多个从节点。MySQL 默 认采用异步复制方式,这样从节点不用一直访问主服务器来更新自己的数据,从节点可以复制主数据库 中的所有数据库,或者特定的数据库,或者特定的表。
mysql主从复制用途:
主从部署必要条件:
在上述三个过程中,涉及了Master的BinlogDump Thread和Slave的I/O Thread、SQL Thread,它们的作用如下:
上述过程都是异步操作,俗称异步复制,存在数据延迟现象。
下图是异步复制的时序图。
解决方法:
为了提升数据安全,MySQL让Master在某一个时间点等待Slave节点的 ACK(Acknowledge character)消息,接收到ACK消息后才进行事务提交,这也是半同步复制的基础,MySQL从5.5版本开 始引入了半同步复制机制来降低数据丢失的概率。
介绍半同步复制之前先快速过一下 MySQL 事务写入碰到主从复制时的完整过程,主库事务写入分为 4 个步骤:
当Master不需要关注Slave是否接受到Binlog Event时,即为传统的主从复制。
当Master需要在第三步等待Slave返回ACK时,即为 after-commit,半同步复制(MySQL 5.5引入)。
当Master需要在第二步等待 Slave 返回 ACK 时,即为 after-sync,增强半同步(MySQL 5.7引入)。
下图是 MySQL 官方对于半同步复制的时序图,主库等待从库写入 relay log 并返回 ACK 后才进行 Engine Commit。
MySQL的主从复制延迟一直是受开发者最为关注的问题之一,MySQL从5.6版本开始追加了并行复制功能,目的就是为了改善复制延迟问题,并行复制称为enhanced multi-threaded slave(简称MTS)。
在从库中有两个线程IO Thread和SQL Thread,都是单线程模式工作,因此有了延迟问题,我们可以采 用多线程机制来加强,减少从库复制延迟。(IO Thread多线程意义不大,主要指的是SQL Thread多线 程)
在MySQL的5.6、5.7、8.0版本上,都是基于上述SQL Thread多线程思想,不断优化,减少复制延迟。
- MySQL 5.6并行复制原理
MySQL 5.6版本也支持所谓的并行复制,但是其并行只是基于库的。如果用户的MySQL数据库中是多个 库,对于从库复制的速度的确可以有比较大的帮助。
基于库的并行复制,实现相对简单,使用也相对简单些。基于库的并行复制遇到单库多表使用场景就发 挥不出优势了,另外对事务并行处理的执行顺序也是个大问题。
- MySQL 5.7并行复制原理
MySQL 5.7是基于组提交的并行复制,MySQL 5.7才可称为真正的并行复制,这其中最为主要的原因就 是slave服务器的回放与master服务器是一致的,即master服务器上是怎么并行执行的slave上就怎样进 行并行回放。不再有库的并行复制限制。
- MySQL 5.7中组提交的并行复制究竟是如何实现的?
MySQL 5.7是通过对事务进行分组,当事务提交时,它们将在单个操作中写入到二进制日志中。如果多 个事务能同时提交成功,那么它们意味着没有冲突,因此可以在Slave上并行执行,所以通过在主库上 的二进制日志中添加组提交信息。
MySQL 5.7的并行复制基于一个前提,即所有已经处于prepare阶段的事务,都是可以并行提交的。这 些当然也可以在从库中并行提交,因为处理这个阶段的事务都是没有冲突的。在一个组里提交的事务, 一定不会修改同一行。这是一种新的并行复制思路,完全摆脱了原来一直致力于为了防止冲突而做的分 发算法,等待策略等复杂的而又效率底下的工作。
InnoDB事务提交采用的是两阶段提交模式。一个阶段是prepare,另一个是commit。
为了兼容MySQL 5.6基于库的并行复制,5.7引入了新的变量slave-parallel-type,其可以配置的值有:
那么如何知道事务是否在同一组中,生成的Binlog内容如何告诉Slave哪些事务是可以并行复制的?
在MySQL 5.7版本中,其设计方式是将组提交的信息存放在GTID中。为了避免用户没有开启GTID功能 (gtid_mode=OFF),MySQL 5.7又引入了称之为Anonymous_Gtid的二进制日志event类型 ANONYMOUS_GTID_LOG_EVENT。
通过mysqlbinlog工具分析binlog日志,就可以发现组提交的内部信息。
可以发现MySQL 5.7二进制日志较之原来的二进制日志内容多了last_committed和 sequence_number,last_committed表示事务提交的时候,上次事务提交的编号,如果事务具有相同 的last_committed,表示这些事务都在一组内,可以进行并行的回放。
- MySQL8.0 并行复制
MySQL8.0 是基于write-set的并行复制。MySQL会有一个集合变量来存储事务修改的记录信息(主键哈 希值),所有已经提交的事务所修改的主键值经过hash后都会与那个变量的集合进行对比,来判断改行 是否与其冲突,并以此来确定依赖关系,没有冲突即可并行。这样的粒度,就到了 row级别了,此时并 行的粒度更加精细,并行的速度会更快。
- 并行复制配置与调优
- COMMIT_ORDERE: 基于组提交机制
- WRITESET: 基于写集合机制
- WRITESET_SESSION: 基于写集合,比writeset多了一个约束,同一个session中的事务 last_committed按先后顺序递增
要开启enhanced multi-threaded slave其实很简单,只需根据如下设置:
slave-parallel-type=LOGICAL_CLOCK
slave-parallel-workers=16
slave_pending_jobs_size_max = 2147483648
slave_preserve_commit_order=1
master_info_repository=TABLE
relay_log_info_repository=TABLE
relay_log_recovery=ON
- 并行复制监控
在使用了MTS后,复制的监控依旧可以通过SHOW SLAVE STATUS\G,但是MySQL 5.7在 performance_schema库中提供了很多元数据表,可以更详细的监控并行复制过程。如下
mysql> show slave status \G;
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 47.94.80.41
Master_User: root
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: mysql-bin.000003
Read_Master_Log_Pos: 1880
Relay_Log_File: mysql-relay-bin.000006
Relay_Log_Pos: 609
Relay_Master_Log_File: mysql-bin.000003
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: 1880
Relay_Log_Space: 816
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: 1
Master_UUID: 29739b4a-f888-11ea-b403-00163e0cb554
Master_Info_File: mysql.slave_master_info
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:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test |
+--------------------+
5 rows in set (0.01 sec)
mysql> use performance_schema;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables like 'replication%';
+---------------------------------------------+
| Tables_in_performance_schema (replication%) |
+---------------------------------------------+
| replication_applier_configuration |
| replication_applier_status |
| replication_applier_status_by_coordinator |
| replication_applier_status_by_worker |
| replication_connection_configuration |
| replication_connection_status |
| replication_group_member_stats |
| replication_group_members |
+---------------------------------------------+
8 rows in set (0.00 sec)
mysql> select * from replication_applier_status_by_worker;
+--------------+-----------+-----------+---------------+-----------------------+-------------------+--------------------+----------------------+
| CHANNEL_NAME | WORKER_ID | THREAD_ID | SERVICE_STATE | LAST_SEEN_TRANSACTION | LAST_ERROR_NUMBER | LAST_ERROR_MESSAGE | LAST_ERROR_TIMESTAMP |
+--------------+-----------+-----------+---------------+-----------------------+-------------------+--------------------+----------------------+
| | 1 | 27 | ON | ANONYMOUS | 0 | | 0000-00-00 00:00:00 |
| | 2 | 28 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 3 | 29 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 4 | 30 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 5 | 31 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 6 | 32 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 7 | 33 | ON | | 0 | | 0000-00-00 00:00:00 |
| | 8 | 34 | ON | | 0 | | 0000-00-00 00:00:00 |
+--------------+-----------+-----------+---------------+-----------------------+-------------------+--------------------+----------------------+
大多数互联网业务中,往往读多写少,这时候数据库的读会首先成为数据库的瓶颈。如果我们已经优化 了SQL,但是读依旧还是瓶颈时,这时就可以选择“读写分离”架构了。 读写分离首先需要将数据库分为主从库,一个主库用于写数据,多个从库完成读数据的操作,主从库之 间通过主从复制机制进行数据的同步,如图所示。
在应用中可以在从库追加多个索引来优化查询,主库这些索引可以不加,用于提升写效率。
读写分离架构也能够消除读写锁冲突从而提升数据库的读写性能。
使用读写分离架构需要注意:主从同步延迟和读写分配机制问题
使用读写分离架构时,数据库主从同步具有延迟性,数据一致性会有影响,对于一些实时性要求比较高 的操作,可以采用以下解决案。
读写路由分配机制是实现读写分离架构最关键的一个环节,就是控制何时去主库写,何时去从库读。目前较为常见的实现方案分为以下两种:
基于编程和配置实现(应用端)
程序员在代码中封装数据库的操作,代码中可以根据操作类型进行路由分配,增删改时操作主库, 查询时操作从库。这类方法也是目前生产环境下应用最广泛的。优点是实现简单,因为程序在代码 中实现,不需要增加额外的硬件开支,缺点是需要开发人员来实现,运维人员无从下手,如果其中 一个数据库宕机了,就需要修改配置重启项目。
基于服务器端代理实现(服务器端)
中间件代理一般介于应用服务器和数据库服务器之间,从图中可以看到,应用服务器并不直接进入 到master数据库或者slave数据库,而是进入MySQL proxy代理服务器。代理服务器接收到应用服 务器的请求后,先进行判断然后转发到后端master和slave数据库。
目前有很多性能不错的数据库中间件,常用的有MySQL Proxy、MyCat以及Shardingsphere等等。