Percona Xtradb Cluster的设计与实现

Percona Xtradb Cluster的设计与实现
 

Percona Xtradb Cluster的实现是在原mysql代码上经过Galera包将不一样的mysql实例链接起来,实现了multi-master的集群架构,以下图所示:php


 
上图中有三个实例,组成了一个集群,而这三个节点与普通的主从架构不一样,它们均可以做为主节点,三个节点是对等的,这种通常称为multi-master架构,当有客户端要写入或者读取数据时,随便链接哪一个实例都是同样的,读到的数据是相同的,写入某一个节点以后,集群本身会将新数据同步到其它节点上面,这种架构不共享任何数据,是一种高冗余架构。
 
通常的使用方法是,在这个集群上面,再搭建一个中间层,这个中间层的功能包括负责根据指点算法找到一个实例供客户端链接,负责使三个实例的负载基本平衡,负责在客户端与实例的链接断开以后重连,也能够负责读写分离(在机器性能不一样的状况下能够作这样的优化)等等,使用这个中间层以后,把这三个实例的架构在客户端方面是透明的,客户端只须要指定这个集群的数据源地址,链接到中间层便可,中间层会负责客户端与服务器实例链接的传递工做。
 
这里其实最核心的问题是,在三个实例之间,由于它们的关系是对等的,multi-master架构的,那么在同时写入的时候,如何保证整个集群数据的一致性,完整性与正确性。
 
在一般mysql的使用过程当中,也不难实现一种multi-master架构,可是通常须要上层应用来配合,好比先要约定每一个表必需要有自增列,而且若是是2个节点的状况下,一个节点只能写偶数的值,而另外一个节点只能写奇数的值,同时2个节点之间互相作复制,由于2个节点写入的东西不一样,因此复制不会冲突,在这种约定之下,能够基本实现多master的架构,也能够保证数据的完整性与一致性。但这种方式使用起来仍是有限制,同时不具备扩展性,不是真正意义上的集群。
 
而经过使用Galera,它在里面经过判断键值的冲突方式实现了真正意义上的multi-master,下面就这个问题展开深刻解析它的实现方式。
 
Percona Xtradb Cluster的实现方式基本上是在Percona MySQL Server的基础上将一个开源包Galera加入进来,实现了数据的同步。
 
Galera提供了不少接口,下面是几个最重要的接口:
 
1.  链接到指定集群名字的集群中,至关因而一个新的节点加入到一个已经存在的集群或者创建一个新的集群
    wsrep_status_t (*connect) (wsrep_t*     wsrep,
                               const char *  cluster_name,
                               const char *  cluster_url,
                               const char *  state_donor,
                               wsrep_bool_t bootstrap);
  
2. 下面这个接口的做用是,在这个函数里面一直接收其它节点广播出来的数据,而且调用复制APPLY函数执行复制操做。这个函数只要返回,说明执行出错或者发生其它问题,只要正常这个函数一直阻塞在这里(具体应该不是阻塞,而是一直在recv函数中执行操做),而里面一直在调用某几个回调函数,回调函数会在初始化的时候指定。
wsrep_status_t (*recv)(wsrep_t* wsrep, void* recv_ctx);
 
3. 下面这个接口的做用是在数据库事务提交时,会使用2阶段提交方式,在ha_trans_commit中,首先会针对每个存储引擎执行一个ht->prepare函数,而对于Galera,在内部实现也是看成一个内嵌的存储引擎使用的,因此它执行的是wsrep_prepare,这个函数的功能是将在执行过程当中产生的binlog经过下面会介绍到的接口 append_data传到其它节点上面去(或者没有传过去,只是将这些数据对象存储在本地,等待提交操做),而后再经过下面这 个接口 pre_commit去与其它节点的Galera通信检查有没有冲突,这个过程也就是在介绍Galera的文章中说到的certification阶段。下面表示出了这个验证过程。
wsrep_status_t (*pre_commit)(wsrep_t*                wsrep,
                                 wsrep_conn_id_t         conn_id,
                                 wsrep_ws_handle_t*      ws_handle,
                                 uint32_t                flags,
                                 wsrep_trx_meta_t*       meta);
 
4. 下面这个接口的做用是,在因为某种缘由致使在复制的过程当中,其它节点已经成功验证,并无冲突,但本节点本身出错了,则此时会经过这个接口模拟复制的过程在本节点将这个事务复制APPLY。
wsrep_status_t (*replay_trx)(wsrep_t*            wsrep,
                                 wsrep_ws_handle_t*  ws_handle,
                                 void*               trx_ctx);
 
5.   下面这个接口是很重要的,在Galera中解决了一个关键问题是不一样对等节点同时作写操做时,若是保证它们之间的冲突的问题。
 wsrep_status_t (*append_key)(wsrep_t*            wsrep,
                                 wsrep_ws_handle_t*  ws_handle,
                                 const wsrep_key_t*  keys,
                                 size_t              count,
                                 enum wsrep_key_type type,
                                 wsrep_bool_t        copy);
 
6. 下面这个接口的做用上面已经提到过了,它是用来将同步数据传递到其它节点上保证全部节点的一致性,这个接口与上面的接口是对应的,至关因而key-value模式,这个是value,上面的是key,基本的做用是,key用来保证每一个节点并发写入时不会冲突(固然冲突时会直接报错回滚),value用来保证每一个节点的数据都是一致的,而对于mysql的实现方式而言,value彻底就是binlog。
    wsrep_status_t (*append_data)(wsrep_t*                wsrep,
                                  wsrep_ws_handle_t*      ws_handle,
                                  const struct wsrep_buf* data,
                                  size_t                  count,
                                  enum wsrep_data_type    type,
                                  wsrep_bool_t            copy);
  
7.    下面这个接口的做用是初始化一个Galera对象,一个实例对应一个这样的对象,在起动服务器的时候初始化,将全部须要的参数,环境变量包括:集群姓名、实例地址,更重要的是,由于Galera拿到的是binlog,它是不知道如何复制数据的,因此这里必需要指定一个接口来作binlog的复制,初始化时告诉Galera,复制数据的接口为 wsrep_apply_cb,那么这个接口接收到的数据就是binlog,它来解析并APPLY这些binlog,到这里时,Galera已经根据这个binlog对应的key作了冲突判断,而且验证经过的。还有一些相似的接口也要在这里指定,后面再作介绍。
   wsrep_status_t (*init)   (wsrep_t*                      wsrep,
                              const struct wsrep_init_args* args);
  
上面这几个接口是最基本的操做接口,这是Galera对外提供的接口。那么如今已经知道这些接口的状况下,就整个mysql工做流程到Galera包的调用说明一下一个PXC事务的工做方式。
 
首先在mysqld启动的时候,执行函数 wsrep_init_startup,这个函数的功能是初始化整个mysql的Galera系统,它主要实现下面一些功能:
  • 在这个函数中首先作的是wsrep_init,这里初始化不少变量,从变量wsrep_provider中找到对应的Galera动态连接包,找到它的接口(包括上面介绍的7个),最主要的是下面结构的初始化:
 
   struct  wsrep_init_args wsrep_args;
 
  struct wsrep_gtid const state_id = { local_uuid, local_seqno };
 
  wsrep_args.data_dir                  = wsrep_data_home_dir;
  wsrep_args.node_name             = (wsrep_node_name) ? wsrep_node_name : "";
  wsrep_args.node_address         = node_addr;
  wsrep_args.node_incoming       = inc_addr;
  wsrep_args.options                   = (wsrep_provider_options) ?   wsrep_provider_options :  "" ;
  wsrep_args.proto_ver               = wsrep_max_protocol_version;
 
  wsrep_args.state_id                  = &state_id;
 
  wsrep_args.logger_cb               = wsrep_log_cb;
  wsrep_args.view_handler_cb   = wsrep_view_handler_cb;
  wsrep_args.apply_cb               = wsrep_apply_cb;
  wsrep_args.commit_cb             = wsrep_commit_cb;
  wsrep_args.unordered_cb         = wsrep_unordered_cb;
  wsrep_args.sst_donate_cb        = wsrep_sst_donate_cb;
  wsrep_args.synced_cb             = wsrep_synced_cb;
 
  rcode = wsrep->init(wsrep, &wsrep_args);
  
这个结构是Galera节点信息初始化的入口,在最后它会经过 wsrep->init(wsrep, &wsrep_args);将当前wsrep初始化,这个wsrep就是当前实例的Galera的全局对象。
wsrep_args.data_dir表示的是当前数据库的目录,对应变量wsrep_data_home_dir
wsrep_args.node_name表示的是当前实例的名字,对应变量wsrep_node_name, 通常默认会被设置为机器名
wsrep_args.node_address表示当前节点的地址,对应变量wsrep_node_address,能够不设置,若是不设置的话,系统会本身找到ip。
wsrep_args.node_incoming表示能够接收链接请求的节点,对应状态变量wsrep_incoming_addresses
wsrep_args.state_id表示一个节点的UUID,对应状态变量wsrep_cluster_state_uuid
 
再下面的是几个回调函数的初始化,须要回调函数的缘由有:
  1. mysql与galera是分层的,上层能够调用galera的接口来复制数据库的,可是对于galera,它是不知道如何提交一个事务,如何判断写入结果集是否是冲突,也不知道如何作sst,更不知道如何解析binlog来作复制。因此上层必需要向galera下层说明如何去解析,如何去执行等。
  2. 框架的通常实现方式,这属于废话。
下面分别介绍每一个回调函数的功能:
  1. wsrep_log_cb:用来作日志的,由于galera产生一些日志后,须要告诉mysql,转换为mysql的日志,因此它须要这个接口告诉mysql,让mysql记录底层产生的日志。
  2. wsrep_view_handler_cb:这是Galera启动以后,第一个须要执行的回调函数,在新节点的Galera加入到而且已经链接到集群中时,新node会拿本身的local_state_uuid和集群的cluster_state_uuid对比,若是这二者有区别时,就须要作sst或者ist,此时这个回调函数所作的工做是,根据不一样的同步方式,准备不一样的同步命令:
    1. 对于mysqldump,这里只须要将数据接收地址及执行方式告诉集群便可。由于对于这种的实现方式是,doner直接在远程链接新加入的mysql实例,将它本身的全部数据dump出来直接用mysql执行了,因此新加入的节点不须要作任何操做,由于doner在导入的时候,若是新节点已经启动完成,也是能够作操做的。
    2. 对于rsync,这里须要作的是告诉集群同步方式及执行命令,由于这与mysqldump不一样,对于rsync与xtrabackup,它们用到的同步方式分别是脚本wsrep_sst_rsync,wsrep_sst_xtrabackup,这2个脚本都放在安装目录的bin下面,在这里会把相应的命令生成出来,对于rsync,相似是这样的命令:
      wsrep_sst_rsync --role 'joiner' --address '192.168.236.231' --auth 'pxc_sst:txsimIj3eSb8fttz' --datadir '/home/q/zhufeng.wang/pxcdata2/' --defaults-file '/home/q/zhufeng.wang/pxcdata2/my.cnf' --parent '11048',
      在这个命令中,--role指明当前节点是joiner仍是doner,由于在脚本中会作不一样处理,--address表示向什么地址同步,--auth是在配置文件中指定的权限信息,--datadir表示当前新加入的库的数据目录,--defaults-file表示新加入节点的配置文件,--parent表示加入者的进程id号。
      这里生成的命令的做用是,以joiner的角色执行脚本wsrep_sst_rsync,从脚本中能够看到,它会给mysql返回一个“ready 192.168.236.231:4444/rsync_sst”的字符串,地址和ip表示的是加入节点上面用来传数据的RSYNC(具体说应该是NC)的地址和端口,4444是脚本中已经写好的值,若是这个端口已经在用了,则会失败。然后面的rsync_sst是用来检查是否是已经有人在作了,若是文件rsync_sst.pid已经存在,则说明正在执行,此时执行会失败。若是正常的话则会建立另外一个文件rsync_sst.conf,这是一个配置文件,用来让加入节点作rsync操做的,里面指定了path,read only,timeout等信息。
      上面的问题解释清楚以后,脚本须要执行的就是等待donor给它传数据了,它会被阻塞,直到数据成功传送完成。
      此时加入节点已经将信息“执行方式+192.168.236.231:4444/rsync_sst“传给了Galera,Galera在获得这个信息,而且等到选择一个合适的donor出来以后,这个donor会给端口4444传数据。脚本等待而且接收数据完成以后,就说明恢复完成了,由于rsync是直接复制文件的。那么此时脚本会返回一个UUID:seqno信息给加入节点,加入节点在这以前一直处于等待状态,若是收到这些信息以后说明恢复完成,则继续启动数据库。
    3. 对于xtrabackup,前面信息都是同样的,只是用到的脚本不一样,脚本是wsrep_sst_xtrabackup,也一样会先执行一个参数角色为joiner的脚本wsrep_sst_xtrabackup,这个部分也会给加入节点返回一个字符串信息:ready 192.168.236.231:4444/xtrabackup_sst,返回以后,脚本就在等待NC将全部donor备份的数据传过来直到完成。仍是一样地,Galera获得192.168.236.231:4444/xtrabackup_sst信息以后,集群会选择一个donor出来给这个地址发送备份数据。等待传送完成以后,脚本会继续执行,此时执行的就是xtrabackup的数据恢复操做,恢复完成以后,脚本会给加入节点输出UUID:seqno信息,而此时加入节点一直在等待的,若是发现脚本给它这些信息以后,说明已经作完sst,那么加入完成,数据库正常继续启动。
  3. wsrep_apply_cb:这个函数其实很容易明白,从名字便可看出是apply,由于在Galera层,上面已经讲过了,它是不知道binlog是什么东西的,它只知道key是什么东西,同时它判断是否是冲突也就是根据key来实现的,根本不会用到binlog,那么在Galera中,若是已经判断到一个节点上面的一个事务操做没有出现冲突,那Galera怎么知道如何去作复制呢?让其它节点也产生一样的修改呢?那么这个函数就是告诉Galera怎么去作复制,在下层,若是判断出不冲突,则Galera直接执行这个回调函数便可,由于这个函数处理的直接就是binlog的恢复操做。它拿到的数据就是一个完成的binlog数据。
  4. wsrep_commit_cb:一样的道理,对于Galera,也是有事务的,它在完成一个操做以后,若是本地执行成功,则须要执行提交,若是失败了,则要回滚,那么Galera也仍是不知道如何去提交,或者回滚,或者回滚提交须要作什么事情,那么这里也是要告诉Galera这些东西。
  5. wsrep_unordered_cb:什么都不作
  6. wsrep_sst_donate_cb:这个回调函数很重要,从名字看出,它是用来提供donate的,确实是的,在上面wsrep_view_handler_cb中介绍的作SST的时候已经介绍了一点这方面的信息,那这个函数是何时用呢?
    它是在
    wsrep_view_handler_cb函数告诉加入节点的地址端口及执行方式以后,这个是告诉pxc集群了,那么集群会选择一个合适的节点去作donor,那么选择出来以后,它怎么知道如何去作?那么这里就是要告诉它如何去作,把数据发送到哪里等等,由于wsrep_view_handler_cb告诉集群的信息只是一个地址、端口及执行方式,是一个字符串而已,那么wsrep_sst_donate_cb拿到的东西就是这个信息,而此时加入节点正处于等待donor传数据给它的状态,应该是阻塞的。
    那么这个回调函数作的是什么呢?首先它会根据传给它的执行方式判断是如何去作,如今有几种状况:
    1. 对于mysqldump方式,这里作的事情是执行下面的命令:              "wsrep_sst_mysqldump "
                    WSREP_SST_OPT_USER " '%s' "
                    WSREP_SST_OPT_PSWD " '%s' "
                    WSREP_SST_OPT_HOST " '%s' "
                    WSREP_SST_OPT_PORT " '%s' "
                    WSREP_SST_OPT_LPORT " '%u' "
                    WSREP_SST_OPT_SOCKET " '%s' "
                    WSREP_SST_OPT_DATA " '%s' "
                    WSREP_SST_OPT_GTID " '%s:%lld'"
      很明显,它如今执行的是脚本  wsrep_sst_mysqldump ,后面是它的参数,其实里面用到的就是mysqldump命令,脚本中主要的操做是mysqldump $AUTH -S$WSREP_SST_OPT_SOCKET --add-drop-database --add-drop-table --skip-add-locks --create-options --disable-keys --extended-insert --skip-lock-tables --quick --set-charset --skip-comments --flush-privileges --all-databases
      能够看出它是将全库导出的,导出以后直接在远程将导出的sql文件经过mysql命令直接执行了,简单说就是远程执行了一堆的sql语句,将全部的信息复制到(远程执行sql)新加入节点中。而这也正是mysqldump不像其它2种执行方式同样在新加入节点还须要作些操做,包括恢复,等待等操做,mysqldump方式在新加入节点也是不须要等待
    2. 对于rsync方式,执行的命令是:wsrep_sst_rsync "
                           WSREP_SST_OPT_ROLE  " 'donor' "
                           WSREP_SST_OPT_ADDR  " '%s' "
                           WSREP_SST_OPT_AUTH  " '%s' "
                           WSREP_SST_OPT_SOCKET  " '%s' "
                           WSREP_SST_OPT_DATA  " '%s' "
                           WSREP_SST_OPT_CONF  " '%s' "
                           WSREP_SST_OPT_GTID  " '%s:%lld'"
      能够看出,它执行的仍是脚本 wsrep_sst_rsync  ,不过如今的角色是 donor,在脚本中,它就是把库下面全部的有用的文件都一块儿用rsync传给加入节点的NC,这又与上面说的接起来了,这边传数据,那么收数据,这边完成以后,那么收数据完成了,则SST也就作完了。
    3. 对于Xtrabackup方式,执行的命令是同样的,只是用到的脚本是wsrep_sst_xtrabackup,道理与上面彻底相同,这边作备份,那么接收数据,等各自完成以后,也就完成了SST。
  7. wsrep_synced_cb:这个回调函数的做用是,在mysql启动的时候,会有一些状态不一致的状况,那么当状态不一致的时候,系统会将当前的状态设置为不可用,也就是wsrep_ready这个状态变量为OFF,那么为了保证数据的一致性,或者因为复制启动的时候会影响到数据的一致性,那么在这个状态的时候,从库的复制线程会等待这个状态变为ON的时候才会继续执行,不然一直等待,那么这个回调函数作的事情就是告诉新加入节点或者donor节点,如今的同步已经完成了,状态已是一致的了,能够继续作下面的操做了。因此此时复制会继续开始。
自从上次说了 wsrep_init_startup以后,感受已通过了好久了,中间大概都有5000个字了。。。
那么如今还接着 wsrep_init_startup继续说:
 
在执行了上面所说的wsrep_init以后,接下来要作的事情是:
执行函数 wsrep_start_replication,这个所作的事情是调用第一个接口 connect,这个函数的功能已经说过了,就是加入集群,算是报个到吧。
接着再作建立一个线程 wsrep_rollback_process,专门作回滚操做。
接着再作 wsrep_create_appliers,专门作APPLY操做,这里建立的线程是 wsrep_replication_process,能够有多个,这个会根据变量wsrep_slave_threads(其实只建立wsrep_slave_threads-1个线程,后面会说到缘由)来建立的,这个就是真正的所谓“多线程复制”。
 
在这个线程中,用到了接口 wsrep->recv(wsrep, (  void  *)thd);
对于函数recv,历来不返回,除非是出错了,这里面的逻辑下面简单说一下:
仍是从上面作SST开始吧:
 
在作SST前,其实只是建立一个线程,若是设置的线程数大于1,则作上面已经说了,会建立wsrep_slave_threads-1个线程,这缘由是:
SST这前,只建立一个,是为了让单独一个线程去处理SST的工做。
 
上面已经说过了,在新加入节点将地址信息发送给集群以后,集群会选择一个donor出来
而建立的那一个单独的线程 wsrep_replication_process会阻塞在 wsrep->recv里面,由于选择出来的donor发现它是新加入的节点,因此它会直接在里面调用回调函数 wsrep_sst_donate_cb,它的参数就是集群给它的“方式+192.168.236.231:4444/rsync_sst”相似的东西,因此这就回到了上面对回调函数 wsrep_sst_donate_cb的说明了,它先作备份,而后再将数据复制到新加入节点中。
 
在作完SST以后,而后把剩下的线程建立起来(若是配置的wsrep_slave_threads大于1的话 )。
 
再接着上面的 wsrep_init_startup函数说:
此时调用最后一个 wsrep_sst_wait,很明显,是等待SST完成,前面已经说过了,新节点一直在等,直到已经作完SST,而后再继续启动数据库。
 
到此为止,谈到的都是Galera的启动过程及SST的东西,下面就正常执行过程讲述一下,能够大概的了解它的原理。
 
首先客户端选择一个集群中的服务器链接上去以后,此时其实与链接一个单点是彻底相同的。
在执行一个sql的时候,在非pxc的状况下,执行的最终调用的函数是 mysql_parse,而对于pxc来讲,它调用的是 wsrep_mysql_parse,对于它们的区别,后面再讲。
 
首先,将一个操做能够分为几个阶段:
  1. 语句分析:对于这个阶段,执行的都是本地操做,不会涉及到集群的操做,因此和普通的mysql执行分析操做没有区别。
  2. 本地执行:对于第2阶段,这里会作一个很重要的操做,那就是在插入、更新、删除记录时会调用一个wsrep_append_keys函数,它的做用就是将当前被修改的记录的关键字(其实就是当前表的关键字在这行记录中对应的列的值,若是没有关键字,则就是rowid,由于pxc只支持innodb)提取出来,再按照Galera接口须要要的格式组装起来,而后再经过Galera的接口append_key(上面已经详细介绍过了)传给集群。
    对于不一样操做,其实KEY的最终数据是不一样的,若是是插入,KEY固然只有新记录的键值,对于更新,包括了旧数据及新数据的键值,对于删除,则只有老数据的键值。同时若是键值具备多个列,则它们的组合方式是以列优先,其次才是新旧值,也就是说,对于更新记录,它的顺序应该是下面这样的:
    旧键第一个值 新键第一个值 旧键第二个值 新键第二个值 ...... ...... 旧键第n个值 新键第n个值
    固然对于插入行,只是简单的下面的格式:
    新键第一个值 新键第二个值 ...... 新键第n个值
    仍是上面说的,galera彻底是靠这些KEY来判断是否是已经和其它客户端的操做形成了冲突的。
     
  3. 事务提交:在这个阶段,首先会作一个两阶段提交的wsrep_prepare,这里面会执行一个核心操做,对应的函数是wsrep_run_wsrep_commit,在这个函数中作了2个重要的操做,首先将这个事务产生的binlog经过接口append_data传给集群,这部分data是与上面的key对应的,组成了key-value结构,key是用来判断是否是冲突的,value是用来复制的,它是完完整整的binlog,在作复制的时候其它节点直接拿来作apply。另外一件事就是调用接口pre_commit,这在上面已经说过了,其实就里请求集群对当前key-value进行验证,若是验证经过,则地本继续执行提交,将数据固化,而若是验证失败,则回滚便可。
那么 如今链接服务器的工做流程已经介绍完,可是对于其它的节点而言,由于集群已经验证经过,本地服务器已经提交完成,那么其它节点在验证经过以后,会在recv接口中继续执行,调用前面说到的 wsrep_apply_cb 回调函数,那么此 时它拿到的就是上面事务提交时的binlog数据,直接作apply就行了。
 
上面提到,在pxc中用到的执行函数为wsrep_mysql_parse,而在普通的mysql中用到的是mysql_parse,这2个的区别是什么呢?首先 wsrep_mysql_parse调用了mysql_parse,只是在它以后,还作了另一些操做,下面就这个操做作一个描述:
 
这里存在一种状况,在当前执行节点的某一个事务在执行pre_commit操做并等待的过程当中,此时有另外一个事务,这个事务有多是来自其它节点的复制事务(在本地表现为复制线程),它的优先级比当前事务高,而且正好这2个事务在某一个数据行上面是冲突的,那么此时就会将当前事务杀死( 其实是调用了一次abort_pre_commit函数,这个函数也是Galera的一个接口,上面没有作过介绍 ),而正在此时,pre_commit已经在其它节点上面完成验证,而且其它节点都已经复制完成,在这种状况下, pre_commit函数会返回一个错误码 WSREP_BF_ABORT,表示其它节点复制完成,当前事务被杀死,那么此时当前链接会一直返回到 wsrep_mysql_parse中,也就是跳出 mysql_parse(由于上面的操做其实都在这个函数中,只是 wsrep_mysql_parse将mysql_parse包了一层 ),执行后 面的操做,这里的操做是在将刚才被杀死的事务从新作一遍,以slave模式作一遍,像复制同样,具体对应的函数为 wsrep_replay_transaction。
 
关于区别,仅此而已。
 
PXC的DDL实现说明
 
下面再详细说一下关于在PXC中的DDL实现方式,这里面可能存在一些问题:
pxc对于DDL,就不那么严谨了,全部的DDL都是经过简单的对象封锁实现的,这里又用到了Galera的另外2个接口,以前没有介绍到的,分别是:
wsrep_status_t (*to_execute_start)(wsrep_t*                wsrep,
                                       wsrep_conn_id_t         conn_id,
                                       const wsrep_key_t*      keys,
                                       size_t                  keys_num,
                                       const struct wsrep_buf* action,
                                       size_t                  count,
                                       wsrep_trx_meta_t*       meta);
 及
wsrep_status_t (*to_execute_end)(wsrep_t* wsrep, wsrep_conn_id_t conn_id);
  
to_execute_start是用来上锁的,主要的参数就是keys,里面的信息就是数据库名及表名等对象名,若是是对库操做,则只有库名,参数 action表示的是当前执行的DDL的语句级的binlog,那么这个是用来复制的,在其它节点直接执行这个binlog就行了。
to_execute_end是解锁的,等中间的操做完成以后,就会调用这个接口。
 
在锁定区间内,由于这是Galera的接口,因此至关于在整个集群中对操做对象作了锁定。
在锁定以后,执行的DDL操做只是本地的,与集群的是没有任何关系的,由于此时其实已经经过一开始的 to_execute_start函数已经在其它节点上面将这个操做已经完成了。
 
关于这一点,很容易测试出来
构造一个2个节点的集群,第一个节点先设置wsrep_on为0,使得它的任何操做不作同步;
如今建立一个表,这个表是本地的,不会复制到另外一个节点上面去。
此时将wsrep_on设置为1
而后再执行一次这个建立表的语句
此时发现,本地的执行报错了,而后再观察一下另外一个节点,居然神奇的出现了?
 
其实这是一种不一致的状态。
上面的测试,若是第二次建立的表名是相同的,可是结构不一样,这就致使2个节点的结构不一样,而建立成功了。
 
上面的测试是说到了另外一个节点某一个表不存在的状况,本地是存在的。
如今若是反过来讲,本地是不存在的,而另外一个节点已经存在,那么按照上面执行的逻辑能够知道,经过 to_execute_start, 另外一个节点的复制确定是不能成功的,由于已经存在了,这点只会在log文件中写一个错误日志表现出来,而没有采起任何其它措施,那个节点仍是正常的。而本地节点则正常的建立这个表,问题仍是同样的。
 
其实上面关于ddl的问题官方的wiki上面已经说清楚了,请参照:
具体这个问题为:
Q: How DDLs are handled by Galera?


Q&A
  1. 若是出现不一致,节点如何被集群踢出
构造下面一种场景:
正常运行的2个节点的集群,分别命名为A和B,如今先在A上面建立一个表
A:create table t (sno int primary key);
B:而后发现B已经同步了这个表
A:设置A节点,让它的修改再也不同步到其它节点,这是一种手动构造不一致的方式
     set wsrep_on=0;
而后在A上面插入一条记录,那么这个记录就不会被复制到B上面
A:insert into t values(1);
此时查看A已经有一条1的记录,而B上面仍是空表,说明是正常的。
如今在B的节点上面插入一条一样的记录,由于上面的wsrep_on只是设置了不从A同步数据到B,而此时B仍是能够同步数据到A的,那么在B上执行一样的插入语句:
B:insert into t values(1);
此时A挂了……
 
分析:这个挂是在recv接口中发生的,由于复制是在这个里面发生的,经过调用回调函数 wsrep_apply_cb实现,而此时A节点的t表已经有一条记录了,B节点再作的时候,这里复制会执行失败,致使 wsrep_apply_cb接口执行时会报错,而后recv知道报错以后,就直接将本身这个进程退出了,这样就形成了节点被中踢出的现象(实际上是本身不想干了)。不过这种表现上是优雅的,由于本身在离开前已经办好了一切离开的手续了,属于正常退出。
 

到如今为止,全部的关于pxc实现方式已经基本搞清楚,其它问题均是mysql自己的问题node

总结:pxc架构上面可谓是实现了真正的集群,最主要的特色是多主的,而且基本没有延时,这样就解决了mysql主从复制的不少问题,但其中仍是存在一些问题的,好比2个节点容易出现脑裂现象,这必需要经过另外一个工具去作一个监控及评判,而官方推荐最好是搞三个节点,但可能会形成必定程度的空间及机器的浪费,但在实际使用时能够适当的在一个实例上面多放置几个库,来提升使用效率。mysql

相关文章
相关标签/搜索