文档章节

使用keepalived来实现MySQL的failover(未完)

realm520
 realm520
发布于 2016/04/06 00:22
字数 4176
阅读 4098
收藏 235

场景介绍

对于小型的应用来说,我们经常使用一个MySQL实例来存放持久化的数据。在这种情况下,如果MySQL崩溃或者安装MySQL的服务器发生了故障,那么整个应用就不可用了。也就是发生单点故障。为了提高系统的可靠性,通常我们可以用两台服务器分别安装MySQL,并且让这两个MySQL实例互为主备。这样,当一台服务器发生了故障,那么另一台服务器可以自动接管数据库连接。

方案介绍

这里我们用keepalived来做failover的检测和自动切换。

虚拟IP: 192.168.1.2
主机1: 192.168.1.3
主机2: 192.168.1.4
MySQL: 5.6.24 (源码编译)
keepalived: 1.2.20

整个网络拓扑如下:

输入图片说明

  • 两台服务器分别安装了keepalived和MySQL
  • keepalived 通过VRRP协议协商出一个MASTER实例,由这个实例对外提供虚拟IP的访问服务
  • 两个MySQL实例之间互为主备,即每个实例都既是主也是备,利用MySQL的replication可以实现 这个架构比较简单,不过也有一些好处:
  1. 对于外部应用来说,只要访问同一个IP地址(虚拟IP)。这样,底层的数据库发生切换时,应用不会感知,也不需要做任何修改。
  2. 互为主备的情况下,一旦主机发生故障,备机可以立刻接管。而当主机恢复后,无需对原来的备机做任何修改,原来的主机可以继续以备机的身份继续运行。

技术要点

MySQL配置

MySQL的安装和启动就不多说,既然已经在用,肯定已经安装好。如果从头部署,那么就自行百度了,编译或者用OS提供的安装包都可以。

MySQL复制(Replication)

Master (192.168.1.3)

  • 启用binary logging (这个配置实际决定了binlog文件的前缀名称)

    log-bin=mysqlbinlog

  • 设置唯一server id

    server-id = 11

  • 设置bin日志刷新频率(可选)

    sync_binlog = 1

  • 创建单独用户

    CREATE USER 'repl'@'%.mydomain.com' IDENTIFIED BY 'slavepass'; 
    
    GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'repl'@'%.mydomain.com'; 
    
- 查看状态 
    ```
mysql> show master status;
+--------------------+----------+--------------+------------------+-------------------+
| File               | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+--------------------+----------+--------------+------------------+-------------------+
| mysqlbinlog.000033 |     3816 |              |                  |                   |
+--------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
  • 查看bin日志

mysql> show master logs; +--------------------+-----------+ | Log_name | File_size | +--------------------+-----------+ | mysqlbinlog.000001 | 120 | | mysqlbinlog.000002 | 332 | | mysqlbinlog.000003 | 242 | | mysqlbinlog.000004 | 120 | ... | mysqlbinlog.000027 | 9544597 | | mysqlbinlog.000028 | 120 | | mysqlbinlog.000029 | 120 | | mysqlbinlog.000030 | 120 | | mysqlbinlog.000031 | 120 | | mysqlbinlog.000032 | 120 | | mysqlbinlog.000033 | 3816 | +--------------------+-----------+ 33 rows in set (0.02 sec)

- 删除bin日志
    ```
purge master logs to 'mysqlbinlog.000003';
  • 重置bin日志

reset master;


#### Slave (192.168.1.4)

- 设置唯一server id

    ```server-id = 1```

- 设置master配置 (这里的MASTER_LOG_FILE和MASTER_LOG_POS的值来自于master实例的“show master status;”的输出)
    ```
CHANGE MASTER TO MASTER_HOST='192.168.1.3', MASTER_PORT=3309, MASTER_USER='repl', MASTER_PASSWORD='slave', MASTER_LOG_FILE='mysqlbinlog.000006', MASTER_LOG_POS=878
  • 启动Slave

START SLAVE;

- 停止Slave
    ```
STOP SLAVE;
  • 查看状态

mysql> show slave status\G; *************************** 1. row *************************** Slave_IO_State: Waiting for master to send event Master_Host: 192.68.1.3 Master_User: test Master_Port: 3306 Connect_Retry: 60 Master_Log_File: mysqlbinlog.000028 Read_Master_Log_Pos: 120 Relay_Log_File: mysqld-relay-bin.000097 Relay_Log_Pos: 285 Relay_Master_Log_File: mysqlbinlog.000028 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: 120 Relay_Log_Space: 459 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: 2 Master_UUID: 17ed3e56-ec5b-11e5-92c3-000c29268615 Master_Info_File: /opt/mysql/instance/data/master.info SQL_Delay: 0 SQL_Remaining_Delay: NULL Slave_SQL_Running_State: Slave has read all relay log; waiting for the slave I/O thread to update it 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 1 row in set (0.00 sec)


### 半同步复制

参考MySQL官方文档:_[http://dev.mysql.com/doc/refman/5.6/en/replication.html](http://)_
我们知道,默认情况下,复制是异步的。那么当应用正在往Master节点写入数据时,主节点挂了,而这时Slave尚未完成binglog的同步。那么这部分数据就会丢失。应用通过重试,可以恢复和Slave的连接,并且继续工作,但是这部分丢失的数据在Slave上是没有的。也就是说,有一部分数据丢失了。

从官方文档我们知道,复制就是异步的,同步的模式只有MySQL Cluster提供。幸好,在MySQL 5.6以后增加了一个半同步的接口。很好的解决了我们的问题。所谓半同步,是指在多个Slave对Master复制的情况下,往Master写入数据时,会话会阻塞,直到至少有一个Slave响应已经复制了该数据,此时才会返回提交结果给应用。在我们这个两节点的情况下,其实就跟同步复制没有区别。

**前提条件**

- MySQL 5.5 以上
- MySQL 支持加载动态库(插件) - show global variables like 'have_dynamic_loading';
- 复制已经工作

**步骤**

- 安装插件
    - master
        ```
        INSTALL PLUGIN rpl_semi_sync_master SONAME 'semisync_master.so';
- slave
    ```
    INSTALL PLUGIN rpl_semi_sync_slave SONAME 'semisync_slave.so';
- 设置全局变量
    - master
        ```
        SET GLOBAL rpl_semi_sync_master_enabled = {0|1}; SET GLOBAL rpl_semi_sync_master_timeout = N;
- slave
    ```
    SET GLOBAL rpl_semi_sync_slave_enabled = {0|1};
- 配置项
    ```
           rpl_semi_sync_master_enabled=1
           rpl_semi_sync_master_timeout=1000 # 1 second
           rpl_semi_sync_slave_enabled=1
  • 重启Slave
        STOP SLAVE IO_THREAD; START SLAVE IO_THREAD;
    
- 查看状态
    ```
        SHOW VARIABLES LIKE 'rpl_semi_sync%';
        SHOW STATUS LIKE 'Rpl_semi_sync%';

操作步骤

Master和Slave上的相关配置和命令都已经在上面列出。接下来就是实际的部署,基本上就是三步:

  1. 修改两个主机的MySQL配置,并启动数据库实例
  2. 锁定两个数据库,并设置住从复制
  3. 确定两个数据库相互都设置了主从之后释放锁

keepalived安装配置

基础知识

这里我们首先了解一下keepalived是个什么软件,能够做什么,后面才能更好的配置和使用它。 百度百科上面描述的并不准确,其实它的官网首页就描述的比较清楚。keepalived是一个路由软件,它主要是为Linux提供负载均衡和高可用。其中负载均衡依赖于LVS,而高可用则是利用VRRP协议来实现的。keepalived本身实现了VRRP协议。

那么这里就很清楚了。我们的方案里面主要是用到高可用,因为我们没有打算让两个MySQL同时提供服务。后面我们配置的时候也只需要配置VRRP相关的配置就可以了。至于LVS,依赖于特定的内核模块,配置相对复杂,我们这里就不多说。

这里再简单说明一下VRRP协议。对于协议本身,我们无需了解过多的细节。对于我们的场景,我们只需要知道,两个主机(或者更多个主机)之间通过VRRP协议协商出一个MASTER,由这个MASTER来对外提供虚拟IP的访问服务。其他的主机就处于BACKUP状态。同时VRRP协议要求定时告知状态给其他主机。当某个主机出现故障,那么其他主机经过一小段时间就会知道。如果故障的是MASTER,那么就会重新协商出一个MASTER。如果是BACKUP故障,那么对MASTER没有影响。

由于VRRP协议是在keepalived内实现的,因此这样的检测能够发现的故障可能是keepalived或者服务器本身的故障。而我们的MySQL服务是否出现故障,通过VRRP协议是无法发现的。因此告诉keepalived,定期的检测MySQL的状态,把MySQL的状态也归到VRRP检测结果中去。这样,当MySQL故障的时候也会引起主备的切换,以达到我们切换MySQL服务的目的。 keepalived本身提供了这个接口,因此对于我们来说只要提供一个检测的脚本给keepalived即可。后面我们会在keepalived的配置一节详细描述。

安装

  • 首先从keepalived.org下载源码,官网也有不少文档可以看看,不过好久都没更新了。
  • Linux下软件的标准编译安装过程
    • ./configure --prefix=/opt/keepalived
    • make
    • make install
  • 这里我安装到/opt/keepalived目录下。整个目录结构如下:

stack@controller:~/keepalived-1.2.20$ tree -d /opt/keepalived/ /opt/keepalived/ ├── bin ├── etc │   ├── keepalived │   │   └── samples │   ├── rc.d │   │   └── init.d │   └── sysconfig ├── sbin └── share └── man ├── man1 ├── man5 └── man8

    - bin 目录下放的是一个genhash文件,这里没有用到
    - etc 目录下放的是配置文件的样例和启动脚本
    - sbin 目录下放的是keepalived主程序
    - share 目录下是一些帮助,运行时无用,可以删除

### 配置
keepalived的配置文件还算简单。所有的配置都存放在一个文件中,由于文件名可以在运行时指定,因此随便放哪里都行。

这里先贴一下我用到的配置,直观的感受一下配置文件长啥样:

global_defs { router_id MySQL-ha } vrrp_script check_mysql { script "mysqladmin -uroot --protocol=tcp ping" interval 10 weight 15 } vrrp_instance VI_1{ state BACKUP
interface eth1 virtual_router_id 51 priority 100
advert_int 1 nopreempt
virtual_ipaddress { 192.168.1.2 } track_script { check_mysql } }

配置文件都是由类似的结构组成:

<块> [名称] { <配置项> <值> }

这些块有些可以嵌套。下面重点解释一下我们用到的选项,其他的如果有兴趣,可以查看源码目录下的一份说明:*keepalived-1.2.20/doc/keepalived.conf.SYNOPSIS*。

1. 首先是global_defs块,里面存放了一些全局的配置,比如通知邮件的收发件人,服务器设置,VRRP协议全局配置等等,这里我们只使用了router_id这个项。主备配成不同名称即可。
2. 然后是vrrp_instance块,这个块可以定义多个,我们只需要一个即可。
    - state 启动的初始状态(MASTER或BACKUP)。这里我们两台都设置为BACKUP,在keepalived启动时通过VRRP协议自行协商谁是MASTER。
    - interface 使用哪块网卡来工作,虚拟IP会绑定在该网卡上(用ifconfig命令看下就行)。
    - virtual_router_id 虚拟路由器id,主备配成一样。
    - priority 优先级,这个跟weight一起工作,决定了哪台主机是MASTER,后面详述。
    - nopreempt 非抢占,当发现优先级比自己低的节点且自己是BACKUP,不切换自己为MASTER。
    - virtual_ipaddress 虚拟IP的地址,主备配成一样。
    - track_script 外部检查脚本,用于检查MySQL服务的状态。脚本的内容由我们自己写,所以也可以检查任何服务。
3. vrrp_script块定义了我们使用的检查命令。这个块的名称被用于vrrp_instance的track_script配置项中。
    - script 定义了检查脚本,可以是一个命令,也可以是一个外部脚本。直接把整个命令放在“”中间就行了。
    - interval 检查的间隔(单位是秒)。
    - weight 结合priority 来实现主备切换。

主要的配置项都了解了,接下来有两个重要的地方特别提一下。

#### 优先级

首先是priority和weight。在我们的配置文件中,这两者的关系是这样的:
- 一个节点的优先级是由priority和weight计算的结果得到
- 当检查脚本执行成功,那么最终的优先级就是priority+weight。以上面的配置为例,最终结果就是115。
- 当检查脚本执行失败,那么最终的优先级就是priority。以上面的配置为例,最终结果就是100。
所有节点上的keepalived通过VRRP协议把这个优先级广播到同一个virtual_router_id内的所有节点,就会得到一个优先级最大节点,那么它就是MASTER。当然根据你的配置,也可能两个节点的优先级一样,此时如何选择,我没有去了解。但是,这个情况我们通过配置可以保证它不出现。

那么对于我们这个两台的情况,就比较简单了。只要确保:
1. 检查脚本正常的情况下,一台主机的优先级比另一台高
2. 一台主机的检查失败时,优先级比另一台成功的要低
在我们的配置中,weight都是15,而两个主机的priority是100和90。也就是两个主机的priority的差的绝对值,小于weight。
这样,初始正常情况下,主机1的优先级是115,主机2的优先级是105。此时主机1为MASTER。而当检查脚本失败时,主机1的优先级变为100,此时主机2切换为MASTER。

有兴趣的话阅读一下源码,应该能了解更加准确的行为:*keepalived-1.2.20/keepalived/vrrp/vrrp_scheduler.c 的 vrrp_update_priority 函数。

#### 非抢占

在优先级一节我们看到,当一个节点发现自己的优先级比其他节点高,那么就会要求切换为MASTER,而原来的MASTER则切换为BACKUP。这个行为可以称作抢占。那么非抢占就是当自身优先级高的情况下,不去要求切换为MASTER。这个行为有什么用呢?

假设我们正常主备两个节点在工作,由于故障,MASTER发生切换。而过了一会儿,原MASTER又恢复了优先级,那么MASTER会再次切换。这里有几个问题要考虑:
1. 这个自动的行为会带来什么后果?
    对MySQL服务来说,应用会来回的对主备节点都做读写。而我们只是做主备,这个行为是否会造成数据不一致,还需测试。但在当前,我们不想让主备同时被访问。
2. 这个自动的来回切换是否是我们期望的?
    在我们的场景下,我们不期望这个行为,因为我们并没有检查到底为什么会发生切换。假如是MySQL的故障,我们并没有对MySQL的故障进行自动恢复。

因此,我们就在要作为主节点的配置文件里加上nopreempt,并且让它的初始priority略高,备节点不加。这样,当启动整个服务时,我们首先启动主节点的服务,然后启动备节点的服务。此时主节点为MASTER。

当主节点发生故障,切换MASTER到备节点。之后就不会在发生切换。如果备节点也故障,那么整个服务都不可用。因此,主节点发生故障时,虽然服务还继续可用,但是我们也要尽快的恢复到正常状态。目前我们还是手工恢复。

### 运行
#### 启动
相对于配置来说,运行非常简单。直接执行命令:

./keepalived -f /opt/keepalived/etc/keeplived/keepalived.conf -D

**-D**参数表示打印更多的日志,生产环境可以不使用。默认keepalived会输出日志到syslog,可以通过/var/log/messages文件来keepalivd的日志。生产环境也注意一下这个日志的回滚。

#### 停止
如果要停止,直接killall keepalived。安装包里自带的启停脚本有点老,不是所有Linux发行版都能用,如果要用就自己修改测试一下。

## 关于脑裂
这里不展开讨论复杂情况的脑裂。在这个环境中,脑裂就是指两个主机都声称自己是VIP的拥有者。比如由于某种原因,导致keepalived无法探测到对方节点,此时两个keepalived都切换自己为MASTER(当然,原先是MASTER的那个节点无需切换)。

### 检测

脑裂问题首先是检测,网上也有一些,我自己总结下来,有以下几个方案:
1. 添加更多的检测手段,比如冗余的心跳线,ping对方等等。尽量减少“裂脑”发生机会。(**指标不治本,只是提高了检测到的概率**)
2. 设置仲裁机制。两方都不可靠,那就依赖第三方。比如启用共享磁盘锁,ping网关等。(**针对不同的手段还需具体分析**)
3. 算法保证,比如采用投票机制(**可惜keepalived没有实现,这个不用想了**)

### 分析及解决
我们分为两类来分析这个问题:
1. 原主备节点只有一个跟外部网络相通
    这个情况下,对应用来讲看到的只是其中的某一个节点,至于是谁是无所谓的。如果应用实际连到的还是主节点,那就没变化。如果应用连到备节点,那就是相当与发生了切换。也就是说对应用没有影响。但是这里也有个问题需要处理,实际不通的那个节点要知道自己不通,并停止自己的keepalived。以免后面又恢复,导致出现问题。
2. 原主备节点只是互相之间的keepalived无法探测彼此,对外连接都还正常。
    这个情况对应用是有影响的,具体如何影响还需测试。但是此时肯定要检测这个情况,并停止其中一个节点。

因此总的来说,首先是检测到这一情况,然后保证一个节点能正常工作,停止另外一个。

但是,如果你的应用,数据库主备节点都接在一个交换机上,而又没有人动你的交换机配置,脑裂发生的概率很小。

© 著作权归作者所有

realm520
粉丝 9
博文 13
码字总数 25892
作品 0
南京
架构师
私信 提问
加载中

评论(13)

realm520
realm520 博主

引用来自“quleap”的评论

建议看一下 http://serverfault.com/questions/361071/what-is-the-difference-between-keepalive-and-heartbeat 。用keepalived来做数据库切换,你就等着脑裂吧。
谢谢提醒,脑裂问题在这个简单拓扑里也有可能出现,这个文档我还会继续更新。后续我测试一下,专门提一下脑裂问题。
hylent
hylent
q
quleap
建议看一下 http://serverfault.com/questions/361071/what-is-the-difference-between-keepalive-and-heartbeat 。用keepalived来做数据库切换,你就等着脑裂吧。
realm520
realm520 博主

引用来自“且听书吟”的评论

主备之间的网络延迟是个大问题,就得看应用对于数据一致性的要求了吧……
对于负载比较高的情况,估计有问题。如果数据量不是太大,延迟应该还好,毕竟数据库一般都是放在内网,至少是千兆带宽。用简单事务测试性能下来,大概有5%左右的损失。还可以接受。至于数据一致性的要求,这个确实不好说。后续再增加一些测试场景测试一下。
且听书吟
且听书吟
主备之间的网络延迟是个大问题,就得看应用对于数据一致性的要求了吧……
走起来
蛮好的
kenyon_君羊
kenyon_君羊
写得不错。补充一下,keepalived需要两边都设置backup模式非抢占才起作用,另外需要考虑主备网络断开造成的vip脑裂,脚本还需要完善网络波动引起的vip乱飘,如增加循环判断的次数。
s
salmon514
玩具而已。
wjdadi
wjdadi
还没写完吧?请继续
easytzb
easytzb
是不是没有写完
mha+keepalived实现mysql master高可用

1.mha概述: MHA是由日本Mysql专家用Perl写的一套Mysql故障切换方案以保障数据库的高可用性,它的功能是能在0-30s之内实现主Mysql故障转移(failover), MHA故障转移可以很好的帮我们解决从...

Magicleesir
2018/06/29
0
0
MHA+MySQL实现mysql的高可用

1. MHA的简单介绍 简介 MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton(现就职于Facebook公司)开发,是一套优秀的作为MyS...

wdw王大为
2018/05/25
0
0
主主互备配合keepalived,MYSQL解决方案

数据库是最基础的数据存储服务之一,需要具备高可用性,实现不同SLA(服务水平协定)的解决方案有很多种,这些方案可用保证数据库服务器子故障时服务继续可用 高可用性需要解决的主要问题有两...

453341288
2016/03/06
127
0
MySQL之MHA+keepalived方案演示(四)

配置VIP实现MHA架构中主库故障自动切换 1.说明 引入keepalived实现MHA架构中主库master故障时,从库slave自动提升为新的maser vip配置可以采用两种方式: 一种通过keepalived的方式管理虚拟i...

wjw555
2018/07/06
0
0
MHA+MySQL实现mysql高可用

1. MHA的简单介绍 简介 MHA(Master High Availability)目前在MySQL高可用方面是一个相对成熟的解决方案,它由日本DeNA公司youshimaton(现就职于Facebook公司)开发,是一套优秀的作为MyS...

wdw王大为
2018/05/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

3_数组

3_数组

行者终成事
今天
7
0
经典系统设计面试题解析:如何设计TinyURL(二)

原文链接:https://www.educative.io/courses/grokking-the-system-design-interview/m2ygV4E81AR 编者注:本文以一道经典的系统设计面试题:《如何设计TinyURL》的参考答案和解析为例,帮助...

APEMESH
今天
7
0
使用logstash同步MySQL数据到ES

概述   在生成业务常有将MySQL数据同步到ES的需求,如果需要很高的定制化,往往需要开发同步程序用于处理数据。但没有特殊业务需求,官方提供的logstash就很有优势了。   在使用logstas...

zxiaofan666
今天
10
0
X-MSG-IM-分布式信令跟踪能力

经过一周多的鏖战, X-MSG-IM的分布式信令跟踪能力已基本具备, 特点是: 实时. 只有要RX/TX就会实时产生信令跟踪事件, 先入kafka, 再入influxdb待查. 同时提供实时sub/pub接口. 完备. 可以完整...

dev5
今天
7
0
OpenJDK之CyclicBarrier

OpenJDK8,本人看的是openJDK。以前就看过,只是经常忘记,所以记录下 图1 CyclicBarrier是Doug Lea在JDK1.5中引入的,作用就不详细描述了,主要有如下俩个方法使用: await()方法,如果当前线...

克虏伯
今天
8
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部