众所周知,mysql是数据库发展史上一颗璀璨的明珠,为了打造高性能高可用的mysql集群,大神们也是挖空心思想法设法去发挥mysql的最大优势。最近正好接触了Galera,正好也简单总结一下,以便对自己学习的东西做个梳理。
首先,什么是集群?这个概念百度一抓一大把,Galera说白了也就是搭建集群的一个工具,更准确的说是在mysql上的一个插件。不同于传统的主从、主备模式,它是多主架构,即一个集群中有N个节点,每个节点都是平等的,你可以从任何一个节点上进行读写操作,而不用担心出现数据不一致的情况。其好处也显而易见,因为主从这种架构决定了它天生就不能保证数据的同步性,而多主模式解决了这个痛点,节点之间不共享任何数据,是一种高冗余的架构。
看到这里,可能你会产生许多疑问,在这里我想重点解决两个问题。
问题1:Galera是怎么保证数据一致性的?数据冲突怎么办?
问题2:新节点怎么加入,加入怎么完成数据的同步?
关于问题1,首先我们要知道,在整个架构中,是分层的
如上图所示,在整个架构中,第一层是数据库层,可以是mysql或者是MariaDB,在往下三层可以看作是接口层,即Galera提供了一系列的API,简称wsrep API,通过这些API,为上层提供了丰富的状态信息和回调函数,进而来实现数据的同步功能。
上图是问题的关键点所在,当客户端向集群中的一个节点node1发起请求时,该事务在node1本地执行成功后,提交之前,会将本次执行的事务形成一个写集,Write-Set,里面包含了此事务执行的信息,是由key和value组成的,key是一些位置信息,比如对那个数据库哪张表哪行数据的操作,value就是具体执行了什么操作。写集的形成自然是调用wsrep API形成的。然后节点1将该写集广播到所有的节点,包括自己本身,不过本身收到写集之后会将写集丢弃而已。然后其他节点之间分别做验证,验证本次操作是不是会有冲突。怎么验证呢?该节点会拿着当前事务的写集的key与当前节点中未完成的事务的所有写集(不仅包括当前节点其他事务产生的写集,还包括其他节点传送过来的写集)的key对比。
同时满足一下三个条件则验证失败:
1,两个事务来源于不同的节点
2,两个事务包含相同的key
3,老事务(GTID大的)相对于新事务还是不可见的,说明老的未提交完成
验证失败后,该事务就会回滚,并不作提交。对于所有的节点都是如此,每个节点单独进行验证,因为每个节点都是一样的,要么全成功,要么都失败。成功后自然就提交了,所有的节点又会重新达到数据一致的状态。由此可见,Galera本身的数据也不是严格同步的,很明显在每个节点上的验证是异步的,这也就是所谓的虚拟同步。
这里我只是简单的介绍,具体还要参考其他资料,因为这个过程确实很复杂。Galera是基于乐观的复制,当涉及到冲突之后,就会调用各种回调函数来处理,最严重的情况就是,某个节点出现严重不一致时,它就会被集群抛弃。
关于问题2
在新节点加入后,有两种方式可以完成数据同步。SST(全量)和IST(增量)。当有Joiner加入时,集群会选择出一个Donor节点去给Joiner传送文件,当然这个节点也是可以通过参数人为指定的。不过IST是有两个条件:
1,Joiner节点的UUID与集群中组节点一致。
2,Joiner节点所缺失的写集可以在Donor的Gcache中存在。
这个UUID你可以简单认为是集群的一个唯一标识,Gcache就是存放写集的区域,可以通过gcache.size来指定。Donor节点决定做什么类型的复制,然后传送相应的文件,Joiner根据接收到的文件进行相应的复制。在这里,复制的方式也是可以设置的,Galera默认rsync,这也是比较快的一种方式,你也可以指定xtrabackup,二者速度差不多,都是物理备份。还有mysqldump,这个是逻辑备份,但是速度慢,不建议使用。
一般来说,IST的速度更快,但是把gcache.size参数设置的远大于数据库状态的大小,IST的效率就会比SST低。