1. MySQL复制的多种工作方式
1.1 异步复制介绍
默认情况下,MySQL的复制就是异步的,即在Master上将所有的更新操作都写入Binlog之后并不能确保所有的更新是否都复制到了Slave服务器的中继日志中,以及是否应用到了Slave数据库里。
异步复制(async replication)的明显优势就是复制效率很高,但是其缺点也十分明显,那就是不同的服务器进行复制时可能会存在数据不一致的问题,甚至还可能会丢失数据;异步方式适合于常规的、普通的互联网应用场景。
1.2 全同步复制介绍
全同步复制(full sync replication)是指当主库执行完一个事务后,需要确保所有的从库都执行了该事务才返回给客户端。因为需要等待所有的从库都执行完该事务才能返回,所以全同步复制的性能必然会受到所有从库更新的拖累。
MySQL自身不支持同步复制,需要用到第三方工具如DRBD(sync模式)等实现同步复制,严格来说,把半同步复制技术默认(或人为)全部应用到所有从库上也算是全同步复制。
同步复制的优点是能够确保将数据实时复制到所有的从库,但是主库需要等待所有从库的写入完成,这会影响主库的更新效率,可能还会导致主库的更新延迟,适合于对数据一致性要求较高的应用场景。
1.3 半同步复制
半同步复制(semi-sync replication)介于异步复制和全同步复制之间,主库在执行完客户端提交的事务之后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,半同步复制最好在低延时的复制节点之间使用。
2. MySQL半同步复制原理及实践准备
2.1 MySQL半同步复制介绍
MySQL从5.5版本开始,即支持半同步复制。引入半同步复制功能的目的是为了保证在Master出现问题的时候,至少有一台Slave的数据是完整的。在超时的情况下半同步复制也会转换为异步复制,以保障主库业务的正常更新。
半同步复制模式在一定程度上可以保证所提交的事务至少会发送给一个从库。但仅仅是保证事务已经传递到了从库上,并不能确保已经在从库上应用完成。
2.2 MySQL半同步复制原理
在半同步复制的模式下,每一个事务都需要等待从库返回正常接收日志数据的信息之后,才会返回给客户更新完毕。如果在更新期间,主从库发生了网络故障或者从库宕机,那么此时主库在事务提交后会等待10秒(默认值),如果10秒内还是不能联系到该从库,就会放弃更新该从库,并向用户返回数据。这时主从库就会恢复到默认的异步复制状态。
如果主机间的通信延迟较小,而且更新的事务较小,则半同步复制可以在主库更新性能损失很小的情况下,最大限度地实现某一台从库的零数据丢失。
如上图所示,要实现半同步复制功能,必须要在主从库中同时开启半同步复制功能,并进行适当的配置才行,在实际工作中,至少需要配置一台半同步从服务器,在主服务器更新事务处理返回给用户之前,需要确认更新已经收到并写入了从服务器的中继日志中。当出现连接从服务器超时(默认为10秒)时,主服务器会暂时切换到异步复制模式进行复制,恢复半同步复制模式的从服务器并及时接收来自主库的更新信息。
2.3 MySQL半同步复制准备
1. MySQL半同步复制环境准备
两台安装好数据库环境的服务器(虚拟机)。
2. MySQL半同步复制服务器角色说明
主库及从库IP、端口信息3. MySQL半同步复制主从库配置文件对比说明
半同步主从库配置文件对比说明表配置完毕后,重启所有的MySQL服务。
4. 配置MySQL主从复制
半同步复制实现的基础是主从复制环境,当然主主复制环境也可以。
3. MySQL半同步复制应用实践
3.1 MySQL半同步复制插件介绍
默认情况下,半同步的插件在MySQL安装目录的如下路径中:
[root@db01 plugin]# pwd
/application/mysql/lib/plugin
[root@db01 plugin]# ll semi*
-rwxr-xr-x. 1 mysql mysql 425183 Jun 7 15:37 semisync_master.so
-rwxr-xr-x. 1 mysql mysql 248303 Jun 7 15:37 semisync_slave.so
3.2 MySQL主库(db01)半同步插件安装和配置
登录MySQL主库(db01)安装半同步插件:
mysql> install plugin rpl_semi_sync_master soname 'semisync_master.so';
Query OK, 0 rows affected (0.05 sec)
开始配置主库半同步插件,临时生效方法具体如下。
控制Master是否开启半同步,需要将如下参数设置为ON或OFF(1或者0):
mysql> set global rpl_semi_sync_master_enabled = ON;
Query OK, 0 rows affected (0.00 sec)
同时控制Master等待多长时间被告知Slave已收到日志,也就是所谓的超时时间:
mysql> set global rpl_semi_sync_master_timeout = 10000;
Query OK, 0 rows affected (0.00 sec)
永久生效方法具体如下。
编辑配置文件调整如下参数到[mysqld]模块下:
[mysqld]
rpl_semi_sync_master_enabled = ON
rpl_semi_sync_master_timeout = 1000
以上两种方法如果同时进行配置,则可以不重启数据库,生产库可以同时配置。
检查主库开启及配置的插件生效方法具体如下:
mysql> select * from mysql.plugin;
+----------------------+--------------------+
| name | dl |
+----------------------+--------------------+
| rpl_semi_sync_master | semisync_master.so |
+----------------------+--------------------+
1 row in set (0.00 sec)
mysql> show plugins; ---也可以查询INFORMATION_SCHEMA.PLUGINS库表
+----------------------------+----------+--------------------+--------------------+---------+
| Name | Status | Type | Library | License |
+----------------------------+----------+--------------------+--------------------+---------+
| binlog | ACTIVE | STORAGE ENGINE | NULL | GPL |
---省略若干---
| partition | ACTIVE | STORAGE ENGINE | NULL | GPL |
| rpl_semi_sync_master | ACTIVE | REPLICATION | semisync_master.so | GPL |
+----------------------------+----------+--------------------+--------------------+---------+
43 rows in set (0.04 sec)
mysql> show global status like '%semi%'; ---查看主库半同步复制的状态参数
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 0 | ---默认值几乎都是0
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 0 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | ON | ---这个为ON,表示开启半同步复制状态
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 0 |
| Rpl_semi_sync_master_tx_wait_time | 0 |
| Rpl_semi_sync_master_tx_waits | 0 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 0 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
mysql> show global variables like '%semi%'; ---查看主库半同步复制的相关参数设置
+------------------------------------+-------+
| Variable_name | Value |
+------------------------------------+-------+
| rpl_semi_sync_master_enabled | ON | ---为ON表示已开启
| rpl_semi_sync_master_timeout | 10000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_no_slave | ON |
+------------------------------------+-------+
4 rows in set (0.00 sec)
3.3 MySQL半同步复制参数介绍
(1)主库半同步复制配置参数说明
主库半同步复制可配置的参数较少,可通过“show global variables like '%semi%';”查看。
(2)主库半同步复制状态说明
主库半同步复制状态的参数较多,可通过“show global status like '%semi%';”查看。
3.4 MySQL主库(db02)半同步插件安装和配置
登录MySQL从库(db02)安装半同步插件:
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
Query OK, 0 rows affected (0.16 sec)
开始配置从库半同步插件,临时生效方法如下。
控制Slave是否开启半同步,需要将如下参数设置为ON或OFF:
mysql> SET GLOBAL rpl_semi_sync_slave_enabled = ON;
Query OK, 0 rows affected (0.00 sec)
永久生效方法如下。
编辑配置文件“/etc/my.cnf”调整如下参数到[mysqld]模块下:
[mysqld]
rpl_semi_sync_slave_enabled = ON
以上两种方法同时配置,则可以不重启数据库,生产库可以同时进行配置。
如果在一个正在运行的Slave上开启半同步复制的功能,那么在配置半同步以后,需要重启停止Slave的I/O线程:
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
---省略若干---
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
---省略若干---
1 row in set (0.00 sec)
mysql> stop slave io_thread; ---停止IO线程
Query OK, 0 rows affected (0.00 sec)
mysql> start slave io_thread; ---启动IO线程
Query OK, 0 rows affected (0.00 sec)
检查主库(db01)开启及配置的插件的生效方法:
mysql> show global status like '%semi%'; ---查看从库半同步复制的状态
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON | ---为ON表示已开启
+----------------------------+-------+
1 row in set (0.05 sec)
mysql> show variables like '%semi%'; ---查看从库半同步复制插件的参数,共2个
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | ON | ---表示是否激活半同步插件
| rpl_semi_sync_slave_trace_level | 32 | ---从库半同步复制的调试级别
+---------------------------------+-------+
2 rows in set (0.04 sec)
同时查看主库db01上面的半同步插件是否开启,输出如下,注意第一个参数clients的值变为1了(之前是0),表示主从半同步复制连接成功。
mysql> show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 0 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 0 |
| Rpl_semi_sync_master_tx_wait_time | 0 |
| Rpl_semi_sync_master_tx_waits | 0 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 0 |
+--------------------------------------------+-------+
14 rows in set (0.04 sec)
3.5 实践1:半同步复制是否标配成功测试
1)登录主库db01再次确定半同步复制插件是否已经开启,状态是否正常。输出如下:
mysql> show global variables like '%semi%';
+------------------------------------+-------+
| Variable_name | Value |
+------------------------------------+-------+
| rpl_semi_sync_master_enabled | ON |
| rpl_semi_sync_master_timeout | 10000 |
| rpl_semi_sync_master_trace_level | 32 |
| rpl_semi_sync_master_wait_no_slave | ON |
+------------------------------------+-------+
4 rows in set (0.00 sec)
mysql> show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 0 |
| Rpl_semi_sync_master_net_wait_time | 0 |
| Rpl_semi_sync_master_net_waits | 0 |
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 0 |
| Rpl_semi_sync_master_tx_wait_time | 0 |
| Rpl_semi_sync_master_tx_waits | 0 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 0 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
方框结尾的值为ON,表示主库半同步插件都已处于工作状态,请注意其他状态值都为0。
2)登录从库db02确定半同步复制插件是否已经开启,并且从库的复制状态是正常的:
mysql> show variables like '%semi%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| rpl_semi_sync_slave_enabled | ON |
| rpl_semi_sync_slave_trace_level | 32 |
+---------------------------------+-------+
2 rows in set (0.00 sec)
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
---省略若干---
Slave_IO_Running: Yes ---确保主从复制是两个Yes
Slave_SQL_Running: Yes
---省略若干---
1 row in set (0.00 sec)
3)开始写入数据:
mysql> create database oldboy11;
Query OK, 1 row affected (0.04 sec)
mysql> create database oldboy12;
Query OK, 1 row affected (0.00 sec)
mysql> show databases like 'oldboy1%';
+---------------------+
| Database (oldboy1%) |
+---------------------+
| oldboy1 |
| oldboy11 |
| oldboy12 |
+---------------------+
3 rows in set (0.01 sec)
4)查看主库半同步复制状态:
mysql> show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 2625 | ---平均网络等待时间为2625毫秒
| Rpl_semi_sync_master_net_wait_time | 5251 | ---总网络等待时间为5251毫秒
| Rpl_semi_sync_master_net_waits | 2 | ---总网络等待次数为2
| Rpl_semi_sync_master_no_times | 0 |
| Rpl_semi_sync_master_no_tx | 0 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 434 | ---平均事务等待时间为434毫秒
| Rpl_semi_sync_master_tx_wait_time | 868 | ---总事务等待时间为868毫秒
| Rpl_semi_sync_master_tx_waits | 2 | ---总事务等待次数为2
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 2 | ---收到了2次接受的确认
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
从主库半同步复制状态的信息上看,复制是成功的。
3.6 实践2:半同步复制超时等待测试
1)停止从库的IO线程复制状态:
mysql> stop slave io_thread; ---测试从库IO线程是否停止,半同步复制更新情况
Query OK, 0 rows affected (0.00 sec)
2)在从主库中更新数据:
mysql> drop database oldboy11;
Query OK, 0 rows affected (10.08 sec) ---此时执行该命令等待了很久(设置了10秒),因为从库已经停止了
mysql> show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 1768 |
| Rpl_semi_sync_master_net_wait_time | 5304 |
| Rpl_semi_sync_master_net_waits | 3 |
| Rpl_semi_sync_master_no_times | 1 | ---失败次数
| Rpl_semi_sync_master_no_tx | 1 |
| Rpl_semi_sync_master_status | OFF | ---半同步状态OFF了
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 434 |
| Rpl_semi_sync_master_tx_wait_time | 868 |
| Rpl_semi_sync_master_tx_waits | 2 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 2 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
mysql> drop database oldboy12;
Query OK, 0 rows affected (0.00 sec)
从上面的测试结果可以得出,从库停止IO线程复制之后,第一次执行删除数据库oldboy11的操作时等待了10秒之后才提交完事务,但是,第二次执行删除数据库oldboy12的操作就很快了,这是因为第一次等待从库超时之后,半同步复制状态自动转换为异步了,所以第二次及以后都会很快了。
3)开启从库的IO线程复制状态:
mysql> start slave io_thread;
Query OK, 0 rows affected (0.00 sec)
4)再次查看主库的半同步复制状态:
mysql> show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 2077 |
| Rpl_semi_sync_master_net_wait_time | 8310 |
| Rpl_semi_sync_master_net_waits | 4 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 2 |
| Rpl_semi_sync_master_status | ON | ---半同步状态ON了
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 434 |
| Rpl_semi_sync_master_tx_wait_time | 868 |
| Rpl_semi_sync_master_tx_waits | 2 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 2 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
因为监测到从库恢复了,因此半同步复制状态自动又恢复为ON,继续保持半同步复制状态了,至此,半同步的测试完成。
3.7 实践3:主从复制故障时的半同步复制测试
1)模拟复制故障:先在从库(db02)上创建oldboy25的数据库,然后在主库(db01)上再创建oldboy25的同名数据库,最后在从库上查看复制状态:
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.150.130
Master_User: rep
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: db01-bin.000002
Read_Master_Log_Pos: 1154
Relay_Log_File: db02-relay-bin.000002
Relay_Log_Pos: 388
Relay_Master_Log_File: db01-bin.000002
Slave_IO_Running: Yes
Slave_SQL_Running: No
Replicate_Do_DB:
Replicate_Ignore_DB:
Replicate_Do_Table:
Replicate_Ignore_Table:
Replicate_Wild_Do_Table:
Replicate_Wild_Ignore_Table:
Last_Errno: 1007
Last_Error: Error 'Can't create database 'oldboy25'; database exists' on query. Default database: 'oldboy25'. Query: 'create database oldboy25'
Skip_Counter: 0
注意:从库配置文件里不能有“slave-skip-errors = 1032,1062,1007,1008,1146,1049”参数行。
2)此时查看主库的半同步复制状态,发现其仍然为ON,如下所示,可见半同步复制和SQL线程没有关系:
mysql> show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 1153 |
| Rpl_semi_sync_master_net_wait_time | 11531 |
| Rpl_semi_sync_master_net_waits | 10 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 2 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 506 |
| Rpl_semi_sync_master_tx_wait_time | 4050 |
| Rpl_semi_sync_master_tx_waits | 8 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 8 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
3)处理从库复制故障:
mysql> stop slave;
Query OK, 0 rows affected (0.01 sec)
mysql> set global sql_slave_skip_counter = 1;
Query OK, 0 rows affected (0.00 sec)
mysql> start slave;
Query OK, 0 rows affected (0.00 sec)
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
---省略若干---
Slave_IO_Running: Yes
Slave_SQL_Running: Yes
---省略若干---
1 row in set (0.00 sec)
4)分别查看主库和从库的半同步复制状态
主库半同步复制状态依然是ON:
mysql> show global status like '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 1153 |
| Rpl_semi_sync_master_net_wait_time | 11531 |
| Rpl_semi_sync_master_net_waits | 10 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 2 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 506 |
| Rpl_semi_sync_master_tx_wait_time | 4050 |
| Rpl_semi_sync_master_tx_waits | 8 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 8 |
+--------------------------------------------+-------+
14 rows in set (0.00 sec)
从库半同步复制状态依然是ON:
mysql> show status like '%semi%';
+----------------------------+-------+
| Variable_name | Value |
+----------------------------+-------+
| Rpl_semi_sync_slave_status | ON |
+----------------------------+-------+
1 row in set (0.00 sec)
4. 生产半同步复制建议及其他方案说明
对于常规的互联网应用,主从复制就够用了,对于主从一致性要求较高的应用可以使用半同步复制方案,这是一个很不错的选择,但是半同步复制方案也会有缺点,那就是半同步复制会导致主库更新性能的下降,尤其是在从库网络不稳定时会对主库更新带来更大的更新性能下降。
1)将主库半同步超时调短(例如,1~2秒)
2)半同步复制的从库硬件与主库之间的网络配置要更好一些
3)半同步从库不用提供任何业务服务(包括读服务)
除了半同步复制技术之外,还有一些方案可以用于解决数据一致性问题:
1)客户端程序实现双写数据库
2)客户端程序在写数据库的同时,写一段时间数据到磁盘上或内存中(redis)
网友评论