MySQL galera cluster

galera cluster是一个MySQL的多主高可用方案,相对于异步复制,最大的长处是不丢数据,因为他是同步复制。

优点与缺点

优点

  1. 真正的多主模式(True Multi-master)
    意味着你可以在任意节点读写,适度的规模可以提高集群整体的性能。
    不会出现主从模式的故障转移(Master-Slave Failover)操作,也不需要VIP。
  2. 同步复制(Synchronous Replication)
    意味着没有slave lag,没有节点crash的时候出现的丢数据现象(Hot Standby)。
    并且是Multi-threaded Slave,性能也不错。
    紧耦合(Tightly Coupled)所有节点数据和状态一致。
  3. 自动节点管理(Automatic Node Provisioning)
    不需要人工去备份数据库恢复到新节点。

参考:"Benefits of Galera Cluster"

我们看看galera的同步复制原理:

  1. 先提交到当前节点的write-set内。
  2. 将write-set改变复制到其他节点。
  3. 到其他节点后,最关键的一步,用Primary key探测是否有冲突,有冲突就rollback,没有就commit,如果有问题的几点怎么办,把它踢出去。

缺点

  1. 只支持InnoDB
    对MyISAM,只支持DDL语句,就是说,创建了表可以在其他节点看到,但插入的数据各是各的。
    为什么是InnoDB?
    多主进行更新的时候,每个节点执行的是乐观策略(假定没有冲突),然后开始更新数据,等到要commit的时候,再问大家是不是有冲突,这样一来,如果有冲突,执行的语句是不是得回滚,而你知道的MyISAM它不支持事务,所以需要支持事务的引擎,而当前MySQL就InnoDB支持事务。
  2. 表里面需要有PK
    如果没有Primary Key,那不同节点DELETE后,可能顺序不一致,表现在select limit语句在不同节点可能返回不一致。
  3. 不支持lock/unlock tables, lock functions (GET_LOCK(), RELEASE_LOCK()... )
    当两个transaction从不同节点更新同一行数据的时候,只有一个transaction会成功,另外一个会返回ER_LOCK_DEADLOCK。
    这其实也没啥大问题,因为逻辑设计上也有问题,不应该将事同一时刻的一个写重定向到两个节点的情况,最好的解决方法是,只有一个节点写。
    当然,这是一件极掉逼格的一件事,人家的多主模式一下子被干成了单主,说出去不好听。
  4. 查询日志不能存到表里,只能存文件。
    log_output=FILE,这个一般也不用,特别是对我来说没啥影响。
  5. 最大transaction size受 wsrep_max_ws_rows,wsrep_max_ws_size两变量控制。
    超出会被reject。准确的说,不是一个缺点。

参考:"Limitations in Galera Cluster for MySQL"

对于上述缺点,在部署之前,就应该考虑清楚能否接受,特别是现有表结构是否有不符合规范的地方,这里有个SQL语句可以查询不符合规矩的表:

如上,我的数据库输出三个没有PK的表。

部署

编译安装就不说了,文档太多。记得几个关键步骤:

  1. 创建mysql用户
  2. 修改相关目录权限
  3. 初始化

配置

最主要的my.cnf信息,差不多这样配置,就能用:

现有数据迁移

比如说从原来的semi-sync方式迁移到galera上,推荐使用原来的master作为first node,其实我觉得,最简单的是,将原来的数据mysqldump出来:

skip-create-options保证了导入数据的时候,用的默认存储引擎,这样一来,原来的MyISAM也会顺利转换为InnoDB。

在galera上导入:

启动顺序

必须要有一个不带连接NODE方式(参数--wsrep-new-cluster)先启动,作为first node,其他节点掉线了,first node还是能正常运行:

运行 SHOW STATUS LIKE 'wsrep_cluster_size'检测是否启动成功(这个结果显示的是多少个节点加入了这个cluster)。

如果集群已经有节点存活,后续启动节点还是按不带连接NODE的方式启动,会提示错误:

在重启整个galera cluster的时候,推荐使用most advanced node作为first node。

  1. Identify the node with the most advanced node state ID.
  2. Start the most advanced node as the first node of the cluster.
  3. Start the rest of the node as usual.

具体参考http://galeracluster.com/documentation-webpages/restartingcluster.html

高可用

准备前端用HAProxy做负载,这样后端节点如果有故障,能自动跳过,而且不需要配置虚拟IP,简化工作,并且可以退一步做读写分离:

关于galera的推荐配置模型,详情可以参考:http://galeracluster.com/documentation-webpages/deploymentvariants.html

HAProxy相关配置可以参考:http://galeracluster.com/documentation-webpages/haproxy.html,此处不再赘述。

故障

状态确认

通过SHOW STATUS LIKE 'wsrep_%'语句,可以查看当前的cluster状态:

这三个可以重点关注:

  1. wsrep_local_state_comment
    Synced表示自己已经和集群连接,并可操作。
  2. wsrep_cluster_size
    查看当前集群的节点个数。
  3. wsrep_ready
    为ON表示可以接客了。

状态分类介绍,参考自《MySQL的Galera Cluster配置说明》

  1. 集群完整性检查
    • wsrep_cluster_state_uuid
      在集群所有节点的值应该是相同的,有不同值的节点,说明其没有连接入集群。
    • wsrep_cluster_conf_id
      正常情况下所有节点上该值是一样的,如果值不同,说明该节点被临时"分区"了。当节点之间网络连接恢复的时候应该会恢复一样的值。
    • wsrep_cluster_size
      当前集群的节点个数,如果这个值跟预期的节点数一致,则所有的集群节点已经连接。
    • wsrep_cluster_status
      集群组成的状态如果不为"Primary",说明出现"分区"或是"split-brain"状况。
  2. 节点状态检查
    • wsrep_ready
      该值为ON,则说明可以接受SQL负载,如果为Off,则需要检查wsrep_connected。
    • wsrep_connected
      如果该值为Off,且wsrep_ready值也为Off,说明该节点没有连接到集群,可能是wsrep_cluster_address或wsrep_cluster_name等配置错造成的,具体错误需要查看日志。
    • wsrep_local_state_comment
      如果wsrep_connected为On,但wsrep_ready为OFF,则可以从该项查看原因。
  3. 复制健康检查
    • wsrep_flow_control_paused
      表示复制停止了多长时间,集群因为Slave延迟而慢多少。该值为0~1之间,越靠近0越好,值为1表示复制完全停止。可优化wsrep_slave_threads来改善。
    • wsrep_cert_deps_distance
      有多少事务可以并行处理,wsrep_slave_threads设置的值不应该高出该这个太多。
    • wsrep_flow_control_sent
      表示该节点已经停止复制了多少次。
    • wsrep_local_recv_queue_avg
      表示slave事务队列的平均长度,slave有没有瓶颈,可以从这里看。最慢节点的wsrep_flow_control_sent和wsrep_local_recv_queue_avg值都是最高的,这两个值越低越好。
  4. 检测慢网络问题
    • wsrep_local_send_queue_avg
      网络瓶颈的预兆,如果这个值比较高的话,可能存在网络瓶颈。
  5. 冲突或死锁的数目
    • wsrep_last_committed
      最后提交的事务数目。
    • wsrep_local_cert_failures/wsrep_local_bf_aborts
      回滚/检测到的冲突数目。

故障模拟

理论上,只要有超过三个的节点,单点故障是不会出现集群不可用的:

  1. 模拟单点服务故障

    此时,其他节点会提示节点掉线,但集群可用:

  2. 模拟单点宕机故障
    crash system
  3. 模拟网络故障
    node1上目前运行了MySQL,并且和204, 208都有连接:

    先确保所有iptables规则已经清除:

    将进出4567端口的链接都断掉:

    此时本机状态会变化,并提示其他节点链接不上:

    数据库也不能使用:

    其他机器会提示状态变化,如下之前的三台变为两台,数据库能使用,但是肯定不会向203同步了:

    删除规则后:

    node1再次上线,数据同步。

常见故障

未配置wsrep_node_address地址
wsrep_node_address地址是用来链接其他节点的地址,如果没有配置,默认取第一个interface上的第一个IP,问题来了,这可能取到一些不正确的IP,例如,我的测试设备取到了127.0.0.1,于是,mysqld进程挂了。
进程堆栈为:

错误日志:

对比一下正确日志:

脑裂

"Split brain" is a condition whereby two or more computers or groups of computers lose contact with one another but still act as if the cluster were intact. This is like having two governments trying to rule the same country. If multiple computers are allowed to write to the same file system without knowledge of what the other nodes are doing, it will quickly lead to data corruption and other serious problems.

一般来说,物理上我们会考虑尽量避免脑裂,例如,为了避免交换机或者网口故障,我们使用双网卡加双交换的方式,做汇聚口提供服务。当然,架构复杂了,成本就上升了。

脑裂出现后,一般的稳定集群的办法:

投票

一般来说票数(不一定是节点数,可能一个节点拥有更多权重,可以占多票),哪个group能占到二分之一以上的票数,则哪个group对资源具有控制权。
在galera里面叫做 WEIGHTED QUORUM机制,实现了这机制,如下图,当左边的节点数大于右边的时候,左边为Primary Component,提供服务:

当然,遇到两边权重一样的,就没得辄了,所以一般会避免节点数为偶数的情况,或者,为偶数的时候,调整为不同的权重:

例如两台机器,这样node2一定不可能是Primary Component:

仲裁

最简单的仲裁模型,例如两节点,谁能PING通网关谁就获得所有权,但是,单纯的PING网关这种仲裁并不严谨,如果两个都能PING通呢?如果两个都不能PING通呢?
所以,一般的仲裁都不是简单的用PING实现,而是在仲裁上,加上资源加锁机制,第一个取得仲裁上资源的节点,获得控制权。
现实中往往不是那么简单,例如获取仲裁资源的节点何时释放资源?冲裁节点如何物理部署?
galera里的仲裁机制叫做Galera Arbitrator,其实可以理解为不落地的模拟节点,由一个叫garbd的模拟进程实现。

fencing

前面的两种机制相对比较友好,咱们公平竞争,fencing的话比较腹黑,一般是利用某种技术把对方搞残,对方残了就不能和我竞争资源了。
这里的某种技术可能是某种能关闭电源的设备,当然,具体得根据场景,例如是两台虚拟机,完全可以是一个POWER OFF的API调用。

这里fencing的动作也是有考虑的,你觉得是reboot好呢还是power off好?假设是reboot,问题没解决,两个节点互相fencing对方,那不成了不停重启了?如果是power off,两个节点互相fencing对方,不就全部挂掉了?
所以,我不太喜欢fencing。

共享资源自带锁

共享资源自带锁,就是在要争取的资源上而不是节点上设置所属标记,这并不适用于所有场景,而且这个标记要用什么姿势来打,值得考虑。
共享资源自带锁,最典型的例子是磁盘锁,两方争抢的是共享磁盘上的文件的时候,我们将锁放在磁盘上,这样谁占有锁谁用就可以了。但是很多时候争取的共享资源不一定是共享文件,比如MySQL的主从复制,我数据并不放在共享存储上,而是放本地,问题出在谁对外提供服务,提供服务必然写数据库,会导致最终又两份数据。

测试

同步效果测试

  1. 在node1上创建表
  2. 在node2上查看DDL语句是否同步过来
  3. 在node2上插入数据

  4. 数据顺利同步到node1

回调脚本

wsrep_notify_cmd选项,可以用于指定回调脚本,回调时会将集群状态等信息当参数传入你的命令行,详情请参考:http://galeracluster.com/documentation-webpages/notificationcmd.html

加入端回调示例:

接受端回调示例:

如果接受失败,还会有一条会滚,例如:

脑裂测试

两节点可能导致脑裂,测试步骤为:

  1. 断开两节点之间的连线。
    此时会发现服务都不能提供服务。
  2. 重新链接网线。
    还是不能提供服务,这个时候,需要在其中一个节点上,重置quorum,集群变得可用:

详情参考:http://galeracluster.com/documentation-webpages/quorumreset.html

参考资料

  1. Ronald Bradford - 《Effective MySQL: Replication Techniques in Depth》
  2. galera官方文档 http://galeracluster.com/documentation-webpages/index.html

PDF下载

MySQL galera cluster》上有 1 条评论

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">