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
再下面的是几个回调函数的初始化,须要回调函数的缘由有:
- mysql与galera是分层的,上层能够调用galera的接口来复制数据库的,可是对于galera,它是不知道如何提交一个事务,如何判断写入结果集是否是冲突,也不知道如何作sst,更不知道如何解析binlog来作复制。因此上层必需要向galera下层说明如何去解析,如何去执行等。
- 框架的通常实现方式,这属于废话。
下面分别介绍每一个回调函数的功能:
- wsrep_log_cb:用来作日志的,由于galera产生一些日志后,须要告诉mysql,转换为mysql的日志,因此它须要这个接口告诉mysql,让mysql记录底层产生的日志。
- wsrep_view_handler_cb:这是Galera启动以后,第一个须要执行的回调函数,在新节点的Galera加入到而且已经链接到集群中时,新node会拿本身的local_state_uuid和集群的cluster_state_uuid对比,若是这二者有区别时,就须要作sst或者ist,此时这个回调函数所作的工做是,根据不一样的同步方式,准备不一样的同步命令:
- 对于mysqldump,这里只须要将数据接收地址及执行方式告诉集群便可。由于对于这种的实现方式是,doner直接在远程链接新加入的mysql实例,将它本身的全部数据dump出来直接用mysql执行了,因此新加入的节点不须要作任何操做,由于doner在导入的时候,若是新节点已经启动完成,也是能够作操做的。
- 对于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信息给加入节点,加入节点在这以前一直处于等待状态,若是收到这些信息以后说明恢复完成,则继续启动数据库。
- 对于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,那么加入完成,数据库正常继续启动。
- wsrep_apply_cb:这个函数其实很容易明白,从名字便可看出是apply,由于在Galera层,上面已经讲过了,它是不知道binlog是什么东西的,它只知道key是什么东西,同时它判断是否是冲突也就是根据key来实现的,根本不会用到binlog,那么在Galera中,若是已经判断到一个节点上面的一个事务操做没有出现冲突,那Galera怎么知道如何去作复制呢?让其它节点也产生一样的修改呢?那么这个函数就是告诉Galera怎么去作复制,在下层,若是判断出不冲突,则Galera直接执行这个回调函数便可,由于这个函数处理的直接就是binlog的恢复操做。它拿到的数据就是一个完成的binlog数据。
- wsrep_commit_cb:一样的道理,对于Galera,也是有事务的,它在完成一个操做以后,若是本地执行成功,则须要执行提交,若是失败了,则要回滚,那么Galera也仍是不知道如何去提交,或者回滚,或者回滚提交须要作什么事情,那么这里也是要告诉Galera这些东西。
- wsrep_unordered_cb:什么都不作
- 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传数据给它的状态,应该是阻塞的。
那么这个回调函数作的是什么呢?首先它会根据传给它的执行方式判断是如何去作,如今有几种状况:
- 对于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方式在新加入节点也是不须要等待
- 对于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也就作完了。
- 对于Xtrabackup方式,执行的命令是同样的,只是用到的脚本是wsrep_sst_xtrabackup,道理与上面彻底相同,这边作备份,那么接收数据,等各自完成以后,也就完成了SST。
- 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,对于它们的区别,后面再讲。
首先,将一个操做能够分为几个阶段:
- 语句分析:对于这个阶段,执行的都是本地操做,不会涉及到集群的操做,因此和普通的mysql执行分析操做没有区别。
- 本地执行:对于第2阶段,这里会作一个很重要的操做,那就是在插入、更新、删除记录时会调用一个wsrep_append_keys函数,它的做用就是将当前被修改的记录的关键字(其实就是当前表的关键字在这行记录中对应的列的值,若是没有关键字,则就是rowid,由于pxc只支持innodb)提取出来,再按照Galera接口须要要的格式组装起来,而后再经过Galera的接口append_key(上面已经详细介绍过了)传给集群。
对于不一样操做,其实KEY的最终数据是不一样的,若是是插入,KEY固然只有新记录的键值,对于更新,包括了旧数据及新数据的键值,对于删除,则只有老数据的键值。同时若是键值具备多个列,则它们的组合方式是以列优先,其次才是新旧值,也就是说,对于更新记录,它的顺序应该是下面这样的:
旧键第一个值 |
新键第一个值 |
旧键第二个值 |
新键第二个值 |
...... |
...... |
旧键第n个值 |
新键第n个值 |
固然对于插入行,只是简单的下面的格式:
新键第一个值 |
新键第二个值 |
...... |
新键第n个值 |
仍是上面说的,galera彻底是靠这些KEY来判断是否是已经和其它客户端的操做形成了冲突的。
- 事务提交:在这个阶段,首先会作一个两阶段提交的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
- 若是出现不一致,节点如何被集群踢出
构造下面一种场景:
正常运行的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