mysql5.7主从复制(1)
Chapter 16 Replication
一、介绍
主从复制允许数据从一个mysql数据库服务器(master)复制到另一个或多个mysql服务器(slave)上。默认情况主从复制是异步执行的;从库不用一直链接到主库上更新主库数据。根据配置,你可以服务所有的数据库或部分的数据库,甚至数据库上的某个表。
Mysql中复制优点包括:
- 横向扩展(Scale-out)解决办法:把负载分散到从库上,以改变性能。在这种情况下,所有的写和更新都在主库上执行。这时可以把读操作放到一个或多个从库上执行。这种模型可以改善写性能(因为主库只负责写)的同时增加读取速度。
- 数据安全:因为数据被复制到从库上,并且从库可以暂停复制进进程,它可以运行备份服务,而不需要中断主库的服务。
- 分析:在线数据存在主库上,同时可以在从库上执行数据分析操作,这样不会影响主库的性能。
- 远距离数据分布:可以用复制功能去创建数据的本地复本,用于远程站点使用,而不需要让他们永久的访问主库。
MySQL5.7支持不同的复制方法。传统的方法是基于从主库的bin log中复制事件,并请求日志文件和日志的当前位置,通过这种方法在主从库中同步数据。最新的方法是基于全局事务标示符(GTIDs),它是事务性质的,因此不需要再请求bin log文件或文件中的位置了,它极大的简化了很多日常复制任务。使用GTIDs的复制,可以保证所有的事务在主库上提交的同时也在从库上提交,这样主从库的一致性得到了保证。
MySQL支持不同类型的同步方法。最初始的同步方法是One-way——异步复制,在这种”同步”方法时,一个服务器做为主库,同时有一个或多个服务器做为从库。
与其相对的另一个同步方法是NDB群集的一个特性。
在MYSQL5.7中,半同步(semisynchronous)复制被支持,并内建到异步复制当中。在使用半同步复制时,主库执行提交会被阻塞,直到至少一个从库已经收到并记录了事务事件之后阻塞才会解除; see Section 16.3.9, “Semisynchronous Replication”。
MYSQL5.7同样支持延迟复制,就像从库故意要在主库之后一段时间再进行操作一样(see Section 16.3.10, “Delayed Replication”.)。
对于同步复制的需求,要使用NDB集群(see Chapter 21, MySQL NDB Cluster 7.5 and NDB Cluster 7.6)
多种复制方法的选择取决于数据和引擎。
这有两种核心复制类型格式,基于复制语句(SBR,Statement Based Replication)它复制所有的SQL语句。和基于复制数据(RBR,Row Based Replication)它复制原始数据。还可以使用第三种,基于混合的复制(MBR,Mixed Based Replication)。不同复制格式的信息 Section 16.2.1, “Replication Formats”
控制复制的选项和变量:Section 16.1.6, “Replication and Binary Logging Options and Variables”.
二、开启主从复制
基于BINARY LOG主从复制法:
Section 16.1.2, “Setting Up Binary Log File Position Based Replication”,
1、在配置文件my.cnf中配置bind-address 0.0.0.0,开启bin log,设置server-id,主从服务器的server-id要设置为不同的值,不然从库不会复制主库数据。
2、在主服务器上建一个用户,赋予replication slave权限:grant replication slave on *.* to repl@'192.168.1.50' ;
3、配置复制信息,在从库上执行:
CHANGE MASTER TO MASTER_HOST='master2.mycompany.com', MASTER_USER='replication', MASTER_PASSWORD='bigs3cret', MASTER_PORT=3306, MASTER_LOG_FILE='master2-bin.001', MASTER_LOG_POS=4,#binlog的位置 MASTER_CONNECT_RETRY=10;
4、在从库上启动复制:
start slave
使用中的问题
1、从库应用relay-log速度慢
并行复制
按顺序并行化:slave_preserve_commit_order=1
并行方式使用“事务组”:slave_parallel_type=LOGICAL_CLOCK
并行线程数:slave_parallel_workers=N
开启log-bin和log-slave-updates(级联更新)这两个选项是必须开启的。
2、数据不一致的问题
1、开启半同步复制
2、使用GTID复制方法
基于GTID事务复制法:
Section 16.1.3, “Replication with Global Transaction Identifiers”
当使用GTID复制法时,每个事务都被标记并跟踪,因为这些在原始主服务器上应用的事务同样要被从服务器应用。在这种方法下一致性可以得到保证。可以在GTID方式中使用statement-based或row-based复制方法(see Section 16.2.1, “Replication Formats”)。但是强烈建议使用row-based格式。
在MYSQL5.7中如何创建一个GTID主从复制
(see Section 16.1.3.1, “GTID Concepts”)
GTID是在主服务器上提交事务时的唯一ID,这个ID不仅仅是在主服务器上是唯一的,它在整个服务器群中也是唯一的,事务和GTID是一一对应的。
GTID是一对由冒号(:)分开的数,如:
GTID = source_id:transaction_id
source_id标示了原始服务器。通常这部分是服务器的server_uuid
。transaction_id是事务的序列号,它由事务提交的顺序决定。如:第一个提交的事务transaction_id是1,那么第10个就是10,这个值不会是0。一个完整的GTID看起来是这个样子的:
3E11FA47-71CA-11E1-9E33-C80AA9429562:23
这个格式的GTID会出现在SHOW SLAVE STATUS
和binary log中。它们同样能在使用mysqlbinlog --base64-output=DECODE-ROWS
查看日志文件或SHOW BINLOG EVENTS
的输出中看到。
SHOW MASTER STATUS
或SHOW SLAVE STATUS
输出的GTID序列会被集成到一个单独表达式,如:
3E11FA47-71CA-11E1-9E33-C80AA9429562:1-5
这个例子显示了第一到第五个事务。服务器的UUID是
3E11FA47-71CA-11E1-9E33-C80AA9429562。
这种格式同样被START SLAVE选项的SQL_BEFORE_GTIDS和SQL_AFTER_GTID支持。
GTID集
GTID集是GTID的集合,如下所示:
gtid_set:
uuid_set [, uuid_set] ...
| ''
uuid_set:
uuid:interval[:interval]...
uuid:
hhhhhhhh-hhhh-hhhh-hhhh-hhhhhhhhhhhh
h:
[0-9|A-F]
interval:
n[-n]
(n >= 1)
GTID集在MYSQL中有几种用法。例如,存储在 gtid_executed
和 gtid_purged
中的系统变量,被描述成GTID集。另外, GTID_SUBSET()
和 GTID_SUBTRACT()
函数以GTID集做为输入。当GTID集从服务器变量中返回时,UUID是以字母和数字升序排列的。
GTID一直用于主从服务器之间。这意味着你可以从BINARY LOG中找到任何事务应用于哪台从服务器上。另外,一但被提交的事务被给予一个GTID,任何子事务都有相同的GTID,而服务器会忽略它们。因此,一个在主服务器上被提交的事务,只能在从服务器上应用一次,这可以保证一致性。
当使用GTID时,从服务器不需要任何非本地数据,如在主服务器上的文件名和该文件的位置(position)。所有和主服务器同步的必要信息都直接从复制数据流中获取。GTID代替了之前用于决定主从服务器从哪里开始,哪里结束或哪里恢复的文件偏移量对。因此,在CHANGE MASTER TO
语句中没有MASTER_LOG_FILE或MASTER_LOG_POST选项;取而代之的是开启MASTER_AUTO_POSITIONT
选项。
GTID的产生和生命周期由以下几步组成:
- 在主服务器上提交并执行一个事务:事务会分配一个GTID(服务器UUID:事务序列号);GTID被写入到主服务器的BINARY LOG中(在事务之前立即写入日志 (immediately preceding the transaction itself in the log))。
- 之后binary log数据被传送到从服务器上并被存储到从服务器的relay log中(建立连接机制过程see Section 16.2, “Replication Implementation”),从服务器读取GTID并以该值设置系统变量
gtid_next
的值。这步告知从服务器下一个事务必须使用这个GTID进行记录。注意:gtid_next是在session上下文中设置的。 - 从服务器验证该GTID并没有在它自己的binary log中被使用过。如果该GTID没有使用过,从服务器会写GTID并应用事务,然后把事务写到它的binary log中。通过先读取检查事务的GTID有没有使用过,可以确定在多客户端时不会并发应用相同的事务多次。
- 因为
gtid_next
非空,从服务器不会尝试为这个事务产生一个自己的GTID,而是使用gtid_next中的值,GTID的值是从主服务器上获取的——在事务持久化到binlog之前获取到(immediately preceding the transaction in its binary log)。
mysql.gtid_executed表
自从MYSQL 5.7.5开始,GTID被存储在一个名为gtid_executed的表中,在mysql库中。表中的每一行记录代表一个GTID或GTID集,原始服务器uuid,开始和结束事务ID;一行记录只有一个GTID,最后两个值是一样的。
mysql.gtid_executed表是在MYSQL服务安装或更新时创建的。(注意不要自己修改这个表)
只有当gtid_mode
是ON或ON_PERMISSIVE时,GTID被存储到mysql.gtid_executed中。GTID存储于该表和bin-log是否开启无关。但是,log bin是否开启会影响GTID的存储方式:
- 如果binary log关闭(log_bin是OFF),服务器存储属于每一个事务的GTID。( the server stores the GTID belonging to each transaction together with the transaction in the table. )另外,当binary log关闭,这个表根据用户配置周基的压缩。see mysql.gtid_executed Table Compression, for more information.
- 如果binary log开启(log_bin是ON),那么在mysql.gtid_executed表中额外存储GTID。无论bin log是否在轮询(is routated)或服务器关闭(server shut down),服务器会为所有已经写入到旧binary log中的日志重新写到带GTID的新binary log中。In the event of the server stopping unexpectedly, the set of GTIDs from the previous binary log is not saved in the
mysql.gtid_executed
table. In this case, these GTIDs are added to the table and to the set of GTIDs in thegtid_executed
system variable during recovery. 当binary log开启时mysql.gtid_executed表不提供完整的GTID记录。这些信息由全局系统变量gtid_executed
提供。
mysql.gtid_executed表可以被 RESET MASTER
重置。
mysql.gtid_executed表压缩
随着时间的推移,mysql.gtid_executed表会被很多无效的GTID堆满,它们都源自同一个服务器,并且它们的事务ID产生一个序列,类似于下面显示的:
mysql> SELECT * FROM mysql.gtid_executed;
+--------------------------------------+----------------+--------------+
| source_uuid | interval_start | interval_end |
|--------------------------------------+----------------+--------------|
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 37 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 38 | 38 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 39 | 39 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 40 | 40 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 41 | 41 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 42 | 42 |
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 43 | 43 |
...
考虑到空间问题,如果表被周期性的压缩,将这些行替换成单独一个记录可以节省很多的空间,如:
+--------------------------------------+----------------+--------------+
| source_uuid | interval_start | interval_end |
|--------------------------------------+----------------+--------------|
| 3E11FA47-71CA-11E1-9E33-C80AA9429562 | 37 | 43 |
...
当GTID开启时,服务器会在mysql.gtid_executed表上周期的执行这样的压缩。你能控制允许执行压缩前有多少个事务,通过设置 executed_gtids_compression_period
系统变量控制压缩频率。这个变量的默认值是1000;这意味着,在默认情况下表执行1000个事务才会执行压缩。设置executed_gtid_compression_period为0,不执行压缩;如果这么做的话gtid_executed表可能会变的非常的大。
注意:当binary log开启时,executed_gtids_compression_period是不使用的,并且mysql.gtid_executed表的压缩在每次binary log 轮询时执行。
mysql.gtid_executed表压缩由一个前台专属进程执行——thread/sql/compress_gtid_table
。这个进程不会出现在SHOW PROCESSLIST,但是可以在threads
表中查看到,如:
mysql> SELECT * FROM performance_schema.threads WHERE NAME LIKE '%gtid%'\G
*************************** 1. row ***************************
THREAD_ID: 26
NAME: thread/sql/compress_gtid_table
TYPE: FOREGROUND
PROCESSLIST_ID: 1
PROCESSLIST_USER: NULL
PROCESSLIST_HOST: NULL
PROCESSLIST_DB: NULL
PROCESSLIST_COMMAND: Daemon
PROCESSLIST_TIME: 1509
PROCESSLIST_STATE: Suspending
PROCESSLIST_INFO: NULL
PARENT_THREAD_ID: 1
ROLE: NULL
INSTRUMENTED: YES
HISTORY: YES
CONNECTION_TYPE: NULL
THREAD_OS_ID: 18677
thread/sql/compress_gtid_table线程通常是休眠状态,直到executed_gtids_compression_period事务执行后,唤醒该进程对mysql.gtid_executed表进行压缩。执行压缩后再次进入休眠状态。executed_gtids_compression_period设置为0表示该线程一直休眠,不被唤醒。
通常如何设置和启动一个基于GTID的复制
(see Section 16.1.3.2, “Setting Up Replication Using GTIDs”)
使用GTID设置复制
设置一个GTID复制技术的关键步骤如下:
- 如果复制已经开始,同步的(主从)服务器是只读的。
- 停止主从服务器
- 使用GTID重新启动服务器并纠正配置选项。
- 通知从服务器以主服务器为复制数据源并使用auto-positioning。
- 做一个新的备份。因为之前的备份不包含GTID,不能用于新配置,所以要做一个新的备份。
- start the slave,再次允许read模式,使它们能接受更新。
下面的例子两个服务器已经运行于主、从服务器,使用的是基于二进制日志位置的复制协议。如果要以一个新服务器开始,先添加一个“复制用户”然后设置 server_id变量。
下面的步骤需要SUPER权限。mysqladmin的shutdown需要SUPER权限或SHUTDOWN权限。
Step 1:同步服务器。这一步需要服务器是同步状态,但没有使用GTID。对于新服务器直接到Step 3。设置系统变量 read_only
为ON,使服务器变为只读:
mysql> SET @@global.read_only = ON;
等待所有的事务提交或回滚。然后从服务器和主服务器完全同部。这一步极其重要,你要确定从服务器已经处理完所有的更新操作。(因为在开启GTID之前的事务是不包含GTID的,如果开启了GTID,但还存在不包含GTID的事务,那么服务器无法识别。)
Step 2:停止主从服务器。使用mysqladmin
停止主从服务器。
shell> mysqladmin -uusername -p shutdown
Step 3:以GTID开启主从服务器。开启基于GTID的复制,通过设置变量 gtid_mode
为ON,启动主从服务器。并开启 enforce_gtid_consistency
变量,确定只有基于GTID的语句才是可以被安全记录的。另外,在配置从服务器之前应该以--skip-slave-start
选项启动从服务器。
开启binary log并非强制。这意味着你的从服务器可以使用GTID,但没有binary log。为了复制主服务器必须一直开启binary log。mysql配置文件中的MYSQLD部分要添加:
gtid_mode=ON
enforce-gtid-consistency=true
Step 4:配置从服务器使用基于GTID的auto-positioning。告知从服务器以基于GTID事务的主服务器为数据源,并使用基于GTID的auto-positioning而不是基于文件位置。在从服务器上使用 CHANGE MASTER TO
语句,包含MASTER_AUTO_POSITION选项在语句中,告知从服务器主服务器的事务以GTID标识。
填写相应的主机地址、端口、用户名、密码,如果已经按step 1设置好并没有什么需要改变的地方,一些选项可以忽略,如下所示:
mysql> CHANGE MASTER TO
> MASTER_HOST = host,
> MASTER_PORT = port,
> MASTER_USER = user,
> MASTER_PASSWORD = password,
> MASTER_AUTO_POSITION = 1;
因为使用了MASTER_AUTO_POSITION=1,所以即不需要使用 MASTER_LOG_FILE
选项,也不需要 MASTER_LOG_POS 选项。如果一起使用了的话会 CHANGE MASTER
TO 语句会执行失败。
Step 5:制做一个新备份。在开启GTID之前的备份已经不能使用了,因为其中不包含GTID信息。
Setp 6:启动从服务器并开启读模式:
mysql> START SLAVE;
如果在setp1中配置了read-only,那么这时必须要取消,为了服务器可以接受更新:
mysql> SET @@global.read_only = OFF;
这时基于GTID的复制就完成了,可以恢复主服务器之前的激活状态了。