Mysql在5.5及其以后的版本引入了半同步的概念,在这里也普及一些基础知识。
一:神马是半同步,同步,异步。
1:Mysql的复制过程就是slave去master拉日志回来,存到relay文件中,然后执行。
2:Master根本不考虑数据是否达到了slave,或者slave是否执行成功了。
3:默认情况下mysql主从复制就是异步的方式,别看好像数据刚被创建,slve就可以看到了,因为你的数据量太小了,无法感受到异步的现象。
4:同步就是两边信息都完全一样,master确认了slvae数据复制并执行成功,才叫同步。
我们举个生产例子说明一下这件事。
生产中是否做过数据库读写分离?不管什么数据库,读写分离就是为了减轻数据库压力,假设我们有2台服务器,A是写入库,B是读库。用户注册了你网站的会员,注册的时候,是通过A库写入, 登录的时候是同步B服务器,对吧?
异步:
注册写到A库,应用前端直接返回注册成功,然后B库才到A库拉取新数据,在本地执行同步。如果由于网络,数据量等原因,B库还没有执行新数据,新用户就点击登录窗口,这个时候,应用前端就提示该用户尚未注册。。蛋疼了吧??
同步情况:
注册写到A库,A库要把信息同步到B库,确定B库执行成功后,返回信息给前端,这个时候应用前端才显示注册成功。用户在注册后登录就不会出现异常了。这才符合逻辑吧?Mysql主从复制中,主库根本不去考虑从库是否把信息拷贝过去,或者成功执行了。
半同步又是什么概念??
如果按mysql主从复制原理看,slave到master把数据垃取下来,然后执行,执行完后,把状态返回给master,这个时候应用程序才给客户端响应成功,这种延迟,用户都接受吗?找一个折中的方法:Mysql半同步在5.5版本横空出世。
半同步情况:
一主一从,一主多从情况下,Master节点只要确认至少有一个slave接受到了事务,即可向发起请求的客户端返回执行成功的操作,master节点是不需要等待slave节点成功执行完这个事务。slave节点接受到这个事务,并成功写入到本地relay日志中,就算是成功了。
半同步在数据完成性上得到了保障,起码主从架构中,有一个备份集,当然,这也不是说半同步配置成功,就不会丢失数据,都是有可能的,比如sql错误,半同步发现错误后,默认会自动转换成半同步的。有利有弊,半同步也增加了成本,对性能也有一定的影响。开源的就是这样。不然为什么Oracle要收费。哈哈。。
二:查看系统是否支持半同步
查看是否加载半同步插件。
sql> show plugins;
查找是否有semisync字母。如果没有跟着步骤走。
步骤1:查找mysql插件目录位置。
sql>show variables like ‘plugin_dir‘;
+---------------+--------------------------------+
| Variable_name | Value |
+---------------+--------------------------------+
| plugin_dir | /usr/local/mysql56/lib/plugin/ |
+---------------+--------------------------------+步骤2:查看目录文件是否存在。
$ll /usr/local/mysql56/lib/plugin/
我们可以发现有2个文件,一个是master.so 一个是slave.so,在主服务器加载master文件,在从服务器加载slave文件就可以。
【实验拓扑】
主机名 | 主机地址 | 角色 |
---|---|---|
node1 | 192.168.2.201 | Master |
node2 | 192.168.2.202 | Slave |
node3 | 192.168.2.203 | Slave |
本文使用CentOS7.1,数据库:MariaDB-5.5.50
注意:本文关闭了selinux,以及iptables。
一、异步复制:
binlog
,从服务器需要开启中继日志relay-log
。 binlog
的主要功能是:记录令数据库内容产生改变的语句,如insert
语句;二进制日志在备份还原的时候至关重要。 relay-log
则是从服务器中开启,作用是从主服务器的二进制日志中复制,并在在从服务器本地执行一次,达到与主服务器内容一致的效果。 (1) 启动二进制日志;
[mysqld]
log_bin=mysql-bin
(2) 为当前节点设置一个全局唯一的ID号;
[mysqld]
server_id=1
(3) 授权创建仅有复制权限的用户账号;
mysql> GRANT REPLCATION SLAVE, REPLICATION CLIENT ON *.* TO 'repuser'@'192.168.2.%' IDENTIFIED BY 'repuser';
(4)查看Master端的二进制日志记录到哪里,用于决定Slave复制的起始位置
MariaDB [(none)]> show master status;
+-------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+-------------------+----------+--------------+------------------+
| Master-log.000001 | 245 | | |
+-------------------+----------+--------------+------------------+
#Slave服务器如果是一个空的数据库而主服务器不为空:
#在同步之前可以先用Master的全量备份,恢复到Slave数据库中。
#然后再从备份那一刻记录的Position开始复制。
#假设Master和Slave都为空,上面的情况就表示Slave从二进制日志Master-log.000001的245开始复制
(1) 启动中继日志;
[mysqld]
relay_log=relay-log
relay_log_index=relay-log.index
(2) 为当前节点设置一个全局惟的ID号;
[mysqld]
server_id=2
#node3把这里改为3
(3) Slave服务器打开只读模式;
[mysqld]
read_only = 1
(4) 使用有复制权限的用户账号连接至主服务器,并启动复制线程;
#注意:上面的是在/etc/my.cnf的配置文件中添加,下面mysql>的则是在mysql服务器中运行的命令
mysql> CHANGE MASTER TO
MASTER_HOST='192.168.2.201',
MASTER_USER='repuser',
MASTER_PASSWORD='repuser',
MASTER_LOG_FILE='Master-log.000001',
MASTER_LOG_POS=245;
(5)在从服务器中开启复制线程
mysql> START SLAVE;
mysql> show slave status\G
*************************** 1. row ***************************
Slave_IO_State: Waiting for master to send event
Master_Host: 192.168.2.201
Master_User: repuser
Master_Port: 3306
Connect_Retry: 60
Master_Log_File: Master-log.000003
Read_Master_Log_Pos: 1379
Relay_Log_File: relay-log.000002
Relay_Log_Pos: 1414
Relay_Master_Log_File: Master-log.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: 1379
Relay_Log_Space: 1702
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
1 row in set (0.00 sec)
插入几条命令之后,从以上的状态信息中可以看得到我们的Master服务器是192.168.2.201
拥有复制功能的账号是repuser,现在复制到Master-log.000003的Position 1379这个位置;
上面的输出结果中,我在复制完成之后使用了flush logs手动地滚动了二进制日志,所以二进制去到000003
二、半同步复制
由于上面已经进行了异步复制的配置,下面仅进行半同步复制的操作。
mysql> INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
插件的文件名字和路径一般在rpm -ql mariadb-server那里查看。
这个插件的库文件是安装好之后就直接有的,只是没有默认安装。
mysql> SET GLOBAL VARIABLES rpl_semi_sync_master_enabled=1;
(1)slave安装插件并修改变量:
mysql> INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
mysql> SET GLOBAL VARIABLES rpl_semi_sync_slave_enabled=1;
这里需要注意的是,node1作为主节点使用的是master模块。node2,node3作为从节点使用slave模块
(2)查看半同步复制的全局变量
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 |
+------------------------------------+-------+
设置rpl_semi_sync_master_enabled=1的效果
第一行是ON则表示半同步复制已经开启。
(3)查看半同步复制的全局变量
mysql> SHOW GLOBAL STATUS LIKE '%semi%';
+--------------------------------------------+-------+
| Variable_name | Value |
+--------------------------------------------+-------+
| Rpl_semi_sync_master_clients | 1 |
| Rpl_semi_sync_master_net_avg_wait_time | 645 |
| Rpl_semi_sync_master_net_wait_time | 645 |
| Rpl_semi_sync_master_net_waits | 1 |
| Rpl_semi_sync_master_no_times | 1 |
| Rpl_semi_sync_master_no_tx | 5 |
| Rpl_semi_sync_master_status | ON |
| Rpl_semi_sync_master_timefunc_failures | 0 |
| Rpl_semi_sync_master_tx_avg_wait_time | 783 |
| Rpl_semi_sync_master_tx_wait_time | 783 |
| Rpl_semi_sync_master_tx_waits | 1 |
| Rpl_semi_sync_master_wait_pos_backtraverse | 0 |
| Rpl_semi_sync_master_wait_sessions | 0 |
| Rpl_semi_sync_master_yes_tx | 1 |
+--------------------------------------------+-------+
第一行Rpl_semi_sync_master_clients显示1,表示有一台主机是半同步复制的状态。
最后需要说明的是,semi复制的MySQL5.7中性能有明显的改善。
假如真的需要使用半同步复制,建议使用5.7的版本。(一般三实例的,从用半同步,从的从用异步)
总结半同步复制:
什么是半同步复制?我们知道在默认情况下,MySQL的复制是异步的,这意味着主服务器及其从服务器是独立的。
异步复制可以提供最佳的性能,因为主服务器在将更新的数据写入它的二进制日志(Binlog)文件中后,
无需等待验证更新数据是否已经复制到从服务器中,就可以自由处理其它进入的事务处理请求。但这也同时带来了很高的风险,
如果在主服务器或从服务器端发生故障,会造成主从数据的不一致,甚至在恢复时造成数据丢失。
半同步复制是从MySQL5.5开始引入了一种半同步复制功能,该功能可以确保主服务器和访问链中至少一台从服务器之间的数据
一致性和冗余。在这种配置结构中,一台主服务器和其许多从服务器都进行了配置,这样在复制拓扑中,
至少有一台从服务器在父主服务器进行事务处理前,必须确认更新已经收到并写入了其中继日志(Relay Log)。当出现超时,
源主服务器必须暂时切换到异步复制模式重新复制,直到至少有一台设置为半同步复制模式的从服务器及时收到信息。
异步复制(Asynchronous replication)
MySQL默认的复制即是异步的,主库在执行完客户端提交的事务后会立即将结果返给给客户端,并不关心从库是否已经接收并处理,这样就会有一个问题,主如果crash掉了,此时主上已经提交的事务可能并没有传到从上,如果此时,强行将从提升为主,可能导致新主上的数据不完整。
全同步复制(Fully synchronous replication)
指当主库执行完一个事务,所有的从库都执行了该事务才返回给客户端。因为需要等待所有从库执行完该事务才能返回,所以全同步复制的性能必然会收到严重的影响。
半同步复制(Semisynchronous replication)
介于异步复制和全同步复制之间,主库在执行完客户端提交的事务后不是立刻返回给客户端,而是等待至少一个从库接收到并写到relay log中才返回给客户端。相对于异步复制,半同步复制提高了数据的安全性,同时它也造成了一定程度的延迟,这个延迟最少是一个TCP/IP往返的时间。所以,半同步复制最好在低延时的网络中使用。
下面来看看半同步复制的原理图:
半同步复制的潜在问题
客户端事务在存储引擎层提交后,在得到从库确认的过程中,主库宕机了,此时,可能的情况有两种
事务还没发送到从库上
此时,客户端会收到事务提交失败的信息,客户端会重新提交该事务到新的主上,当宕机的主库重新启动后,以从库的身份重新加入到该主从结构中,会发现,该事务在从库中被提交了两次,一次是之前作为主的时候,一次是被新主同步过来的。
事务已经发送到从库上
此时,从库已经收到并应用了该事务,但是客户端仍然会收到事务提交失败的信息,重新提交该事务到新的主上。
无数据丢失的半同步复制
针对上述潜在问题,MySQL 5.7引入了一种新的半同步方案:Loss-Less半同步复制。
针对上面这个图,“Waiting Slave dump”被调整到“Storage Commit”之前。
当然,之前的半同步方案同样支持,MySQL 5.7.2引入了一个新的参数进行控制-rpl_semi_sync_master_wait_point
rpl_semi_sync_master_wait_point有两种取值
AFTER_SYNC
这个即新的半同步方案,Waiting Slave dump在Storage Commit之前。
AFTER_COMMIT
老的半同步方案,如图所示。
半同步复制的安装部署
要想使用半同步复制,必须满足以下几个条件:
1. MySQL 5.5及以上版本
2. 变量have_dynamic_loading为YES
3. 异步复制已经存在
1、MySQL的异步复制和半同步复制 - 简书 http://www.jianshu.com/p/d877cbe9f0f0
2、Mysql5.6.21半同步 http://www.mamicode.com/info-detail-576734.html
3、MySQL数据的主从复制、半同步复制和主主复制详解 - CSDN博客 http://blog.csdn.net/goustzhu/article/details/9339621
4、MySQL异步复制、半同步复制详解 - paul_hch - 博客园 http://www.cnblogs.com/paul8339/p/6879329.html
5、MySQL半同步复制 - iVictor - 博客园 http://www.cnblogs.com/ivictor/p/5735580.html